SpringBootJWT实现token验证并将用户信息存储到注解内

5次阅读

共计 5093 个字符,预计需要花费 13 分钟才能阅读完成。

springboot 集成 jwt 实现 token 验证
1、引入 jwt 依赖
    <!--jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.9.0</version>
        </dependency>
2、自定义两个注解
/**
 * 忽略 Token 验证
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreAuth {boolean required() default true;
}
/**
 * 登录用户信息
 *
 */
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {boolean required() default true;
}

@Target: 注解的作用目标
@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.METHOD)——方法
3、定义一个用户实体类

/**
 * 用户类
 *
 */

@Data
@ApiModel(value="User 对象", description="用户表")
public class User extends BaseEntity<User> {

private static final long serialVersionUID=1L;

    @ApiModelProperty(value = "编号")
    private String id;

    @ApiModelProperty(value = "归属公司")
    private String companyId;

    @ApiModelProperty(value = "归属部门")
    private String officeId;

    @ApiModelProperty(value = "登录名")
    private String loginName;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "工号")
    private String no;

    @ApiModelProperty(value = "姓名")
    private String name;

    @ApiModelProperty(value = "邮箱")
    private String email;

    @ApiModelProperty(value = "电话")
    private String phone;

    @ApiModelProperty(value = "手机")
    private String mobile;

    @ApiModelProperty(value = "用户类型")
    private String userType;

    @ApiModelProperty(value = "用户头像")
    private String photo;

    @ApiModelProperty(value = "最后登陆 IP")
    private String loginIp;

    @ApiModelProperty(value = "最后登陆时间",example = "2019-11-22 00:00:00")
    private Date loginDate;

    @EnumFormat
    @ApiModelProperty(value = "登录状态 : 0 正常,1 异常")
    private UserLoginFlagEnum loginFlag;

    @ApiModelProperty(value = "创建者")
    private String createBy;

    @ApiModelProperty(value = "创建时间",example = "2019-11-22 00:00:00")
    private Date createDate;

    @ApiModelProperty(value = "更新者")
    private String updateBy;

    @ApiModelProperty(value = "更新时间",example = "2019-11-22 00:00:00")
    private Date updateDate;

    @ApiModelProperty(value = "备注信息")
    private String remarks;

    @TableLogic
    @ApiModelProperty(value = "删除标记")
    private String delFlag;

    @ApiModelProperty(value = "微信 openid")
    private String openid;


    @Override
    protected Serializable pkVal() {return this.id;}

}
4、生成 token
@Service("TokenService")
public class TokenService {public String getToken(User user) {
        String token="";
        token= JWT.create().withAudience(user.getId())// 将 user id 保存到 token 里面
                .sign(Algorithm.HMAC256(user.getOpenid()));// 以 OpenId 作为 token 的密钥
        return token;
    }
}
5、设置拦截器
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
    @Autowired
    IUserService userService;

    public static final String LOGIN_USER_KEY = "LOGIN_USER_KEY";


    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        // 支持跨域请求
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with,X-Nideshop-Token,X-URL-PATH");
        httpServletResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));

        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        

        // 如果不是映射到方法直接通过
        if(!(object instanceof HandlerMethod)){return true;}
        HandlerMethod handlerMethod=(HandlerMethod)object;
        Method method=handlerMethod.getMethod();
        // 检查是否有 IgnoreAuth 注释,有则跳过认证
        if (method.isAnnotationPresent(IgnoreAuth.class)) {IgnoreAuth passToken = method.getAnnotation(IgnoreAuth.class);
            if (passToken.required()) {return true;}
        }
        // 检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(LoginUser.class)) {LoginUser userLoginToken = method.getAnnotation(LoginUser.class);
            if (userLoginToken !=null) {
                // 执行认证
                if (token == null) {throw new RuntimeException("无 token,请重新登录");
                }
                // 获取 token 中的 user id
                String userId;
                try {userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {throw new RuntimeException("401");
                }
                // 设置 userId 到 request 里,后续根据 userId,获取用户信息
                httpServletRequest.setAttribute(LOGIN_USER_KEY, userId);

                User user = userService.getById(userId);
                if (user == null) {throw new RuntimeException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getOpenid())).build();
                try {jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {throw new RuntimeException("401");
                }
                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception { }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {}}
6、配置拦截器

在配置类上添加了注解 @Configuration,标明了该类是一个配置类并且会将该类作为一个SpringBean 添加到 IOC 容器内

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");   
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {return new AuthenticationInterceptor();
    }
}
7、token 验证流程

1、用户登录是生成 token
2、从 http 请求头中取出 token
3、判断是否映射到方法
4、检查是否有 @IgnoreAuth 注释,有则跳过认证
5、检查是否有用户登录的注解,有则需要取出并验证
6、认证通过则可以访问

正文完
 0