• SpringBoot+JWT@注解实现token验证


    原文链接: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、认证通过则可以访问

    1. public class JWTUtil {  
    2.     public static final String SECRET_KEY = "123456"; //秘钥  
    3.     public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; //token过期时间  
    4.     public static final long REFRESH_TOKEN_EXPIRE_TIME = 10 * 60 * 1000; //refreshToken过期时间  
    5.     private static final String ISSUER = "issuer"; //签发人  
    6.   
    7.     /** 
    8.      * 生成签名 
    9.      */  
    10.     public static String generateToken(String username){  
    11.         Date now = new Date();  
    12.         Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法  
    13.           
    14.         String token = JWT.create()  
    15.             .withIssuer(ISSUER) //签发人  
    16.             .withIssuedAt(now) //签发时间  
    17.             .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) //过期时间  
    18.             .withClaim("username", username) //保存身份标识  
    19.             .sign(algorithm);  
    20.         return token;  
    21.     }  
    22.       
    23.     /** 
    24.      * 验证token 
    25.      */  
    26.     public static boolean verify(String token){  
    27.         try {  
    28.             Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法  
    29.             JWTVerifier verifier = JWT.require(algorithm)  
    30.                     .withIssuer(ISSUER)  
    31.                     .build();  
    32.             verifier.verify(token);  
    33.             return true;  
    34.         } catch (Exception ex){  
    35.             ex.printStackTrace();  
    36.         }  
    37.         return false;  
    38.     }  
    39.       
    40.     /** 
    41.      * 从token获取username 
    42.      */  
    43.     public static String getUsername(String token){  
    44.         try{  
    45.             return JWT.decode(token).getClaim("username").asString();  
    46.         }catch(Exception ex){  
    47.             ex.printStackTrace();  
    48.         }  
    49.         return "";  
    50.     }  
    51. }  
  • 相关阅读:
    MIME类型大全
    Asp.net中解决“请求超时”的问题
    C#日期函数所有样式大全
    [转]Oracle数据关联查询
    convert时间格式转换参数表 [收藏]
    vs2008安装失败。解决办法。部分。
    asp.net获取web.config配置信息
    jQuery UI Dialog控件中的表单无法正常提交的解决方法
    控制Button在数据验证成功才执行后台方法
    关于使用DataTable.Compute()方法时报“聚合参数中的语法错误: 需要具有可能的“Child”限定符的单个列参数。”
  • 原文地址:https://www.cnblogs.com/fswhq/p/13634248.html
Copyright © 2020-2023  润新知