JJWT是一个提供端到端的JWT创建和验证的Java库

依赖

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>RELEASE</version>
        </dependency>

token的创建

setIssuedAt用于设置签发时间
signWith用于设置签名秘钥

        JwtBuilder builder = Jwts.builder().setId("111")
                .setSubject("小明")
                .setIssuedAt(newDate())
                .signWith(SignatureAlgorithm.HS256, "ld");

        String token = builder.compact();

token的解析

        String token = "~~~";
        Claims claims = Jwts.parser().setSigningKey("ld").parseClaimsJws(token).getBody();
        System.out.println("id:" + claims.getId());
        System.out.println("subject:" + claims.getSubject());
        System.out.println("IssuedAt:" + claims.getIssuedAt());

token过期校验

        long now = System.currentTimeMillis();  //当前时间
        long exp = now + 1000 * 60; //过期时间为1分钟
        JwtBuilder builder = Jwts.builder().setId("111")
                .setSubject("小明")
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, "ld")
                .setExpiration(new Date(exp));

当未过期时可以正常读取
当过期时会引发 io.jsonwebtoken.ExpiredJwtException 异常

自定义claims

        long now = System.currentTimeMillis();  //当前时间
        long exp = now + 1000 * 60; //过期时间为1分钟
        JwtBuilder builder = Jwts.builder().setId("111")
                .setSubject("小明")
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, "ld")
                .setExpiration(new Date(exp))
                .claim("role", "admin");

获取:
    claims.get("role")

示例

JWT工具类

@Data
public class JwtUtil {

    private String key; //密钥加盐

    private long ttl;   //过期时间

    /**
     * 生成JWT
     */
    public String createJWT(String id, String subject, String role) {
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        JwtBuilder builder = Jwts.builder().setId(id)
                .setSubject(subject)
                .setIssuedAt(now)
                .signWith(SignatureAlgorithm.HS256, key).claim("role", role);
        if (ttl > 0) {
            builder.setExpiration(new Date(nowMillis + ttl));
        }
        return builder.compact();
    }

    /**
     * 解析JWT
     */
    public Claims parseJWT(String jwtStr) {
        return Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(jwtStr)
                .getBody();
    }
}


添加配置

jwt:
  config:
    key: littledonkey
    ttl: 3600000


签发token

        //判断是否密码是否正确
        Admin loginAdmin = adminService.login(admin);
        if (loginAdmin == null) {
            return new Result(false, StatusCode.LOGINERROR, "登陆失败");
        }
        //签发token
        String token = jwtUtil.createJWT(admin.getId(), admin.getLoginname(), "admin");
        HashMap<String, String> map = new HashMap<>();
        map.put("token", token);
        map.put("role", "admin");
        return new Result(true, StatusCode.OK, "登陆成功", map);

使用拦截器方式实现token鉴权

org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,
继承此类,可以非常方便的实现自己的拦截器。
三个方法分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面):

  1. 在preHandle中,可以进行编码、安全控制等处理
  2. 在postHandle中,有机会修改ModelAndView
  3. 在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录
添加拦截器

@Component
public class TokenInterceptor implements HandlerInterceptor {
    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取请求头(如果有此请求头,表示token已经签发)
        String header = request.getHeader("tokenHeader");
        if (header != null || !"".equals(header)) {
            //解析请求头(防止伪造token,token内容以"token "作为开头)
            if (header.startsWith("token ")) {
                try {
                    Claims claims = jwtUtil.parseJWT(header.substring(6));
                    String role = (String) claims.get("role");
                    //为具有相关权限的用户添加权限到request域中
                    if ("admin".equals(role)) {
                        //拿到"admin_token"头信息,表示当前角色是admin
                        request.setAttribute("admin_token", header.substring(6));
                    }
                    if ("user".equals(role)) {
                        //拿到"user_token"头信息,表示当前角色是user
                        request.setAttribute("user_token", header.substring(6));
                    }
                } catch (Exception e) {
                    throw new RuntimeException("令牌不正确");
                }
            }
        }
        //所有请求都通过,具体权限在service层判断
        return true;
    }
}


注册拦截器

@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
    @Autowired
    private TokenInterceptor tokenInterceptor;

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login/**");
    }
}


service层验证

    public void deleteById(String id) {
        String admin_token = (String) request.getAttribute("admin_token");
        if(admin_token == null || "".equals(admin_token)){
            throw new RuntimeException("权限不足");
        }
        adminDao.deleteById(id);
    }