likes
comments
collection
share

快速入门JWT+Redis实现Token验证!

作者站长头像
站长
· 阅读数 15

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

导言:该文最终本人实现目的为通过JWT来生成token作为用户登录或调用api等身份验证凭证,同时简单的引入了Redis作为缓存来存放Token。用最简洁的方法给第一次接触Token的朋友们快速入门并且实际运用到项目中。不纠结于理论,只聚焦于实战,如果有希望了解更多理论的朋友,可以在看本文的过程中查询其他理论性文章。

这几天毕设项目正准备开始搞,想到之前的项目登录没有做token密码只是md5加密,再加上实习导师的建议,我就开始搞了JWT+Redis,查了诸多参考文献资料等,花了三天时间基本完成了这个功能。

快速入门JWT+Redis实现Token验证!

Json Web Token

引入JWT

<!--        Json web token 用于验证-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

配置JWT工具类

Token分为三个部分,头,荷载,和签名,我们需要对这几个部分分别设置,第一个方法为创建一个JsonWebToken

第二个工具类为检查token是否正常,值得注意的是,除了解析后的姓名身份等等,如果时间异常(已经超过有效期)也将被catch异常,所以直接写在try-catch中即可

//JsonWebTokenUtil

public class JsonWebToken {

    private static longtime= 1000*60*60*24;
    private static Stringsignature= "9991morF0GgnahZ";

    public static String createToken(String role,String name){
        JwtBuilder jwtBuilder = Jwts.builder();
        String jwToken = jwtBuilder
                //链式编程 添加头
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                //payload 载荷
                .claim("name",name)
                .claim("role",role)
                //主题
                .setSubject("admin")
                //有效期
                .setExpiration(new Date(System.currentTimeMillis()+time))
                .setId(UUID.randomUUID().toString())
                //signature签名
                .signWith(SignatureAlgorithm.HS256,signature)
                //拼接前面三个
                .compact();
        return jwToken;
    }

    public static boolean checkToken(String token){
        if (Objects.equals(token, "") || token == null){
            return false;
        }

        try {
            //解析后拿到存有token信息的集合
            Jws<Claims> claimsJws1 = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
        } catch (Exception e) {
            System.out.println("【Error】token解密异常");
            e.printStackTrace();
            return false;
        }
        return true;
//        Claims claims = claimsJws1.getBody();
//        System.out.println(claims.get("name"));
//        System.out.println(claims.get("role"));
//        System.out.println(claims.getId());

    }
}

在application.yml中

spring:
    redis:#配置redis
    host: 127.0.0.1#公网ip
    port: 6379#端口一般为6379
    connect-timeout: 5000
    database: 1#数据库

在控制层对应URL中使用

只展示与Token相关核心代码

adminLogin 为登录VO内有phone password token三个字段,最后返回时将password清空保证安全(并非唯一方式也并非最佳方式此返回仅供参考)

getOne 为本人写的获取用户信息的函数,并非本文重点内容,在此声明不做阐述。

//声明redis
@Autowired
private StringRedisTemplate stringRedisTemplate ;


String name = getOne(queryWrapper).getAdminName();
//下面为核心代码 
//获取Token
adminLogin.setToken(JsonWebToken.createToken("admin",name));
//将用户token写入Redis缓存 以token为key与value前端不用多传值,后两个参数为过期时间
stringRedisTemplate.opsForValue().set(adminLogin.getToken(),adminLogin.getToken(),1000*60*60*24*7, TimeUnit.SECONDS);
//上面为核心代码
adminLogin.setPassword("");
return JsonResultUtils.successMsg("登录成功",adminLogin);

完成到这一步之后,恭喜你已经完成了基础的token配置(可以配合解析token直接用了)但是,一般来说我们用户使用过程中会让有些功能需要token,有些功能不需要token,那具体那些需要哪些不需要呢?我们需要在后台做一个URL的区分,这个时候就需要用到拦截器

拦截器的配置

第一步需要先写好拦截的控制方法

在下面代码中已有对应的详细注释,只对个别点做单独说明

Authorization 为前端设置的在请求头中放置的token值,后续会有说明前端如何设置

@Component
public class TokenHandler implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 注意:放行浏览器的预检请求
        System.out.println("进入预检");
        if (request.getMethod().equals("OPTIONS")) {
            return true;
        }
        // 获取请求头中携带的token值
        String token = request.getHeader("Authorization");
        // token验证
        //查看token是否为空 查看token是否合法
        if(JsonWebToken.checkToken(token)) {
            //查看Redis中有无token
            String value = stringRedisTemplate.opsForValue().get(token);
            if (Objects.equals(value, token)) {
                System.out.println("【success】token与redis中的匹配成功");
                return true;
            }
        }
            System.out.println("【error】 token与redis中的匹配失败");
//            throw new Exception("【error】 token与redis中的匹配失败");
            // 验证失败,拦截请求
            return false;
    }

}

第二步我们需要去写好拦截的配置

第一个方法为拦截器方法,在注释中已经讲的很明白了,如有多个排除拦截URL,用逗号分隔,或者写在List中

第二个方法为跨域,反正你先这样配上,控制层用注解去跨域,因为根据查阅的部分资料文章说这个跨域可能会影响token,我没碰到过不清楚,反正先配上hh

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private TokenHandler tokenHandler;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加tokenHandler拦截器 全部拦截 排除"/admin/login"
        registry.addInterceptor(tokenHandler).addPathPatterns("/**").excludePathPatterns("/admin/login");
    }
		
		//跨域配置
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  // 允许跨域请求的地址
                .allowedOriginPatterns("*")  // 允许跨域请求的域名
                .allowedMethods("GET", "POST", "PUT", "DELETE", "PATH")  // 允许请求的方式
                .allowCredentials(true)  // 是否允许证书(cookies)
                .maxAge(3000);  // 跨域允许时间
    }

}

至此为止你的token就已经大功告成了!

前端配置!

配置axios的request方法中拦截request方法

注意:如果你出错了,请检查你是否拿到token时把其存入了localStorage!

Request.interceptors.request.use(config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';
    // 在浏览器的 Session Storage 中拿到 token 值
    config.headers.Authorization =window.localStorage.getItem("token");
    return config;
})

Redis

引入Redis启动器

<!-- 配置使用redis启动器,存储token -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

如果出错!请想一想你装Redis没,装了开没,开了ping没,ping了pong没。

如果一切正常但是依旧报错,请移步搜索,因为我没踩过坑…

本文尚未设计点

  1. 前端Router路由时是否需要拦截? 解决方案:设置路由守卫,后端新开一个checktoken的api,跳转时询问后端。
  2. 相信不完美的地方,尚未考虑到的地方还有很多,请大家多多指教,将持续改进更新本文。