原文链接:https://segmentfault.com/a/1190000022776284?utm_source=tag-newest
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、认证通过则可以访问
- public class JWTUtil {
- public static final String SECRET_KEY = "123456"; //秘钥
- public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; //token过期时间
- public static final long REFRESH_TOKEN_EXPIRE_TIME = 10 * 60 * 1000; //refreshToken过期时间
- private static final String ISSUER = "issuer"; //签发人
- /**
- * 生成签名
- */
- public static String generateToken(String username){
- Date now = new Date();
- Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法
- String token = JWT.create()
- .withIssuer(ISSUER) //签发人
- .withIssuedAt(now) //签发时间
- .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) //过期时间
- .withClaim("username", username) //保存身份标识
- .sign(algorithm);
- return token;
- }
- /**
- * 验证token
- */
- public static boolean verify(String token){
- try {
- Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法
- JWTVerifier verifier = JWT.require(algorithm)
- .withIssuer(ISSUER)
- .build();
- verifier.verify(token);
- return true;
- } catch (Exception ex){
- ex.printStackTrace();
- }
- return false;
- }
- /**
- * 从token获取username
- */
- public static String getUsername(String token){
- try{
- return JWT.decode(token).getClaim("username").asString();
- }catch(Exception ex){
- ex.printStackTrace();
- }
- return "";
- }
- }