springboot 2.1 实践教程(二十)-整合JWT

JWT基础介绍,请访问:一分钟搞懂JWT

SpringBoot整合JWT,本次教程使用JWT场景是用户登录后,服务端生成Jwt Token,并返回至客户端,当客户端再次访问系统时需携带jwt Token,发送请求后,服务端会校验jwt token是否正确则运行客户访问系统,否则提示用户token信息异常,无法访问系统。

其实最终的目的就是通过jwt token 来判断用户是否认证通过系统。

那么在这里我们会思考以下几个问题:

1、当客户进行登录操作,并成功后?我们如何创建jwt Token?

2、如何将jwt token 返回给客户端

3、客户端一般如何携带jwt token 发送至服务端

4、服务端如何验证该jwt token 是否有效?

带着以上几个问题我们开始整合JWT

  1)首先在pom.xml中引入jwt的坐标

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.2.0</version>
</dependency>

通过以上引入的java-jwt包我们可以方便的对jwt token进行创建和获取

2)创建用户登录的页面login.html

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="login">
    <h1>登录</h1>
    <font color="red" th:text="${msg}"></font>
    <form class="layui-form"  method="post" action="/user/login">
        <div class="layui-form-item">
            <input class="layui-input" name="userName" value="admin" placeholder="用户名"  type="text"
                   autocomplete="off">
        </div>
        <div class="layui-form-item">
            <input class="layui-input" name="password" value="" placeholder="密码"
                   type="password" autocomplete="off">
        </div>

        <input type="submit" value="登录">
    </form>
</div>
</body>

</html>

3)编写后台登录的处理器Controller

package org.learn.controller;

import com.alibaba.fastjson.JSONObject;
import org.learn.jwt.JwtUtil;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
@RequestMapping("/user")
public class UserController {

    /**
     * 用戶控制器
     *
     * @return
     */
    @RequestMapping("/login")
    @ResponseBody
    public JSONObject submitLogin(String userName, String password, HttpServletRequest req, HttpServletResponse rep, Model model) throws Exception {
        JSONObject jsonObject = new JSONObject();
        if (userName.equals("test1") && "123456".equals(password)) {
            String token = JwtUtil.createToken("userInfo", userName);//创建token
            jsonObject.put("token", token);
        } else {
            jsonObject.put("msg", "用户名或密码错误!");
        }

        return jsonObject;
    }


}

从login方法中我们可以看到,当用户信息校验成功后,我们就可以创建token了,我们可以封装个JwtUtil用于进行jwt token的管理,具体代码如下

package org.learn.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类,包括jwt的创建、验证等方法
 */
public class JwtUtil {
    private static final String SECRET = "org.learnjwt.secret";  //秘钥
    private static final String ISSUER = "jwtlearn";
    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);


    /**
     * 生成token
     *
     * @param username 用户名
     * @return 返回生成的token字符串
     */
    public static String createToken(String infoJson, String username) throws Exception {
        int second = 100000;  //jwt token有效期  毫秒
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        // 附带username信息
        return JWT.create()
                .withIssuer(ISSUER)  //jwt签发者
                .withClaim("username", username)  //用户名
                .withExpiresAt(DateUtils.addSeconds(new Date(), second))  //有效日期
                .withSubject(infoJson)  //用户信息
                .sign(algorithm);//签名算法


    }

    /**
     * 校验token是否正确
     *
     * @param token TOKEN
     * @return boolean
     */
    public static Map verify(String token, String username) {
        Map result = new HashMap<String, Object>(2);
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("username", username)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            String subject = jwt.getSubject();
            result.put("username", username);
            result.put("isSuccess", true);
            result.put("ex", null);
            result.put("subject", subject);
            return result;
        } catch (Exception e) {
            logger.info("鉴权失败", e);
            result.put("isSuccess", false);
            result.put("exception", e);

        }
        return result;

    }


    /**
     * 获得token的到期时间
     *
     * @return token中包含的签发时间
     */
    public static Date getIssuedAt(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUER).build();
            DecodedJWT jwt = verifier.verify(token);
            return jwt.getExpiresAt();
        } catch (Exception e) {
            logger.error("获取有效时间失败", e);
            return null;
        }
    }

    /**
     * 获得token的到期剩余时间 毫秒
     *
     * @return token中包含的签发时间
     */
    public static long getLiveTime(String token) {
        try {
            Date date = getIssuedAt(token);
            if (date != null) {
                return (date.getTime() - new Date().getTime());
            }
        } catch (Exception e) {
            logger.error("获取剩余有效时间失败", e);
            return 0;
        }
        return 0;
    }


    /**
     * token是否过期
     *
     * @return true:过期
     */
    public static boolean isTokenExpired(String token) {
        Date now = Calendar.getInstance().getTime();
        Date issuedAt = getIssuedAt(token);
        return issuedAt == null ? true : issuedAt.before(now);
    }

    /**
     * 生成随机盐,长度32位
     *
     * @return
     */
/*
    public static String generateSalt() {
        SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
        String hex = secureRandom.nextBytes(16).toHex();
        return hex;
    }
*/


    /**
     * 无需解密直接获得token中的用户名
     *
     * @return token中包含的用户名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            e.printStackTrace();
            return null;
        }
    }


}

下面解决第二个问题,如何将创建好的jwt token 返回给客户端?

一般解决方案是,将生成好的jwt token 设置到请求头中,返回给客户端

rep.setHeader("token",token);

第三个问题当客户端获取到jwt token,再次请求系统,那么如何携带token呢?

解决方案是,客户端将token设置到cookie中或请求头,然后向服务端发起请求

第四个问题,服务端时如何校验token的呢?

解决方案是,创建一个过滤器,用于过滤用户的请求,在这个过滤器中,获取用户的token,然后校验该token是否合法,如果合法则运行访问系统,如果不合法。则返回异常信息给用户。

具体过滤器代码如下:

package org.learn.filter;

import org.apache.commons.lang3.StringUtils;
import org.learn.jwt.JwtUtil;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JwtFilter implements Filter {


    /**
     * 排除链接
     */
    public List<String> excludes = new ArrayList<>();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        {   //排除的连接(不进行过滤的连接)
            String tempExcludes = filterConfig.getInitParameter("excludes");
            if (StringUtils.isNotEmpty(tempExcludes)) {
                String[] url = tempExcludes.split(",");
                for (int i = 0; url != null && i < url.length; i++) {
                    excludes.add(url[i]);
                }
            }

        }
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;
        //url过滤
        if (handleExcludeURL(request, response)) {
            filterChain.doFilter(request, response);
            return;
        }
        //获取header中的token
        final String token = request.getHeader("jwtToken");
        if (token == null) {
            response.getWriter().write("token不存在!");
            return;
        }

        String userName = JwtUtil.getUsername(token);
        Map<String, Object> repMap = new HashMap<>();
        if (null == userName) {
            response.getWriter().write("token异常!");
            return;
        } else {
            repMap = JwtUtil.verify("token", userName);//验证token

            if (null != repMap && !repMap.isEmpty()) {
                userName = (String)repMap.get("username");
                //如果token合法,则放行
                request.getSession().setAttribute("userName", userName);

            } else {
                response.getWriter().write("token不合法!");
                return;
            }

        }
        filterChain.doFilter(req, res);

    }

    @Override
    public void destroy() {

    }

    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {

        if (excludes == null || excludes.isEmpty()) {
            return false;
        }
        String url = request.getServletPath();
        for (String pattern : excludes) {
            Pattern p = Pattern.compile("^" + pattern);
            Matcher m = p.matcher(url);
            if (m.find()) {
                return true;
            }
        }
        return false;
    }
}

具体实现,就在doFilter方法中,核心代码

String userName = JwtUtil.getUsername(token); //根据token 获取用户名
   repMap = JwtUtil.verify("token", userName);//验证token

这样我们就可以验证token 是否合法了。

具体完整代码可以访问我的gitee获取  完整源码获取

猜你喜欢

转载自blog.csdn.net/java_cxrs/article/details/104717698