网站首页 > 技术文章 正文
这篇讲一下若依框架认证所用的jwt技术。若依前后端分离的框架,不同于以前的单体的web应用的架构,可以把session存储在服务器的内存中,浏览器访问的时候通过cookie携带相关的认证信息,服务器可以去通过cookie里面的值去判断该请求是否已经经过认证。这个 session技术也有一定的弊端,就是session被存储在了服务器内容中,对于内存有较大的压力,并且后台有多个server的情况下,还需要负载均衡有会话保持的功能。
对于session的存储,也有把 session存储在redis中,这样虽然解决了对于服务器内存的压力,和服务器无状态的情况,但是这样使得系统对于redis的依赖也加重了。
由于现在采用了前后端分离的技术,后台服务器不会直接与客户端接触,后台服务器只是会与前端服务器进行交互,一般性的会话保持肯定是不可行的,后端的服务器对于前端发过来的请求肯定是没有保持状态一说了,所以有了不同于session的认证技术了。
若依框架使用了jwt(Json Web Token)技术,来进行登录和鉴权。主要的流程有这么几步:
登录成功之后,服务器端验证通过返回jwt的字符串给客户端
客户端再发起请求之后,会在请求头里面带上jwt的内容
服务器验证jwt中的内容这个与session的区别在于服务器端不会存储生成的token,服务器负责生成和解析。
我们可以通过浏览器的开发者模式观察一下,登录之后的返回内容:
返回的那个token就是后续用于鉴权使用的。可以看到jwt的格式是一个字符串,用"."分隔成3部分。
若依框架用了jjwt这个库来实现了token的生成和解析。
       <!-- Token生成与解析-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>首先关注一下工程中配置文件的部分,首先定义了在http请求头中的标识,令牌的密钥,和有效时长。
# token配置
token:
    # 令牌自定义标识
    header: Authorization
    # 令牌密钥
    secret: abcdefghijklmnopqrstuvwxyz
    # 令牌有效期(默认30分钟)
    expireTime: 30下一步,我们准备分析一下框架里面的源码,主要的关注的功能是两个,一个是生成token,另外一个则是解析。TokenService.java这个类是主要的要关注的。首先是从配置文件中读取token相关的配置。
 // 令牌自定义标识
    @Value("${token.header}")
    private String header;
    // 令牌秘钥
    @Value("${token.secret}")
    private String secret;
    // 令牌有效期(默认30分钟)
    @Value("${token.expireTime}")
    private int expireTime;createToken这个方法是核心的一个方法,用于生成token,这里很多都是调用了现有的一些工具,我们主要关注一下实现的流程。首先产生一个随机id,然后再进行一些登录信息的设置,然后再更新token,最后调用jwt 的工具生成token,签名算法是 HS512。
    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser)
    {
        String token = IdUtils.fastUUID();
        loginUser.setToken(token);
        setUserAgent(loginUser);
        refreshToken(loginUser);
        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        return createToken(claims);
    }
private String createToken(Map<String, Object> claims)
    {
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }再看一下验证token的源码。会先获取当前的系统时间,,相差不足20分钟,自动刷新缓存,通过刷新缓存的方法,会把内容刷入redis中。
    public void verifyToken(LoginUser loginUser)
    {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            refreshToken(loginUser);
        }
    }下一部分是验证token:
验证token是结合了SpringSecurity,使用了JwtAuthenticationTokenFilter这个过滤器来进行解析。
第一步是获取请求里面的token,从里面获取对应的用户和权限信息。
    public LoginUser getLoginUser(HttpServletRequest request)
    {
        // 获取请求携带的令牌
        String token = getToken(request);
        if (StringUtils.isNotEmpty(token))
        {
            try
            {
                Claims claims = parseToken(token);
                // 解析对应的权限以及用户信息
                String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                String userKey = getTokenKey(uuid);
                LoginUser user = redisCache.getCacheObject(userKey);
                return user;
            }
            catch (Exception e)
            {
            }
        }
        return null;
    }里面的一步是获取请求头里面的token
    private String getToken(HttpServletRequest request)
    {
        String token = request.getHeader(header);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
        {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }获取到token 之后就是解析,这里也是调用现有的库。
    private Claims parseToken(String token)
    {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }解析完之后,后面就是验证token,有效的话就会刷新时效,如果是无效的话,会在过滤器中抛出异常。
再用户退出之后,若依框架对于token的处理是对token进行删除,相关的代码在LogoutSuccessHandlerImpl.java中。
@Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException
    {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser))
        {
            String userName = loginUser.getUsername();
            // 删除用户缓存记录
            tokenService.delLoginUser(loginUser.getToken());
            // 记录用户退出日志
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
        }
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功")));
    }总结一下,这里使用了jwt作为认证的框架,jwt是否比传统的session-cookie一定适合web应用还是存在疑问的,本篇文章仅仅研究很分析了一下这个框架使用jwt的基本流程。
猜你喜欢
- 2024-12-25 Spring Boot整合Spring Cloud GateWay代理第三方应用的调用接口?
 - 2024-12-25 Java 近期新闻:Hibernate 6.0、JobRunr 5.0、JHipster 7.8.0
 - 2024-12-25 Keycloak Servlet Filter Adapter使用
 - 2024-12-25 如何在Spring Boot中保证RESTful接口的安全性?
 - 2024-12-25 Java项目实战第6天:登录业务的实现
 - 2024-12-25 JavaEE概述总结:Servlet生命周期+JSP内置对象
 - 2024-12-25 SpringBoot 无感刷新 Token springboot的token
 - 2024-12-25 Spring MVC中提供了哪些扩展机制?如何使用这些扩展机制?
 - 2024-12-25 49个Spring经典面试题总结(附带答案)
 - 2024-12-25 互联网项目的进步--前后端是怎么分离的?
 
- 最近发表
 - 
- 聊一下 gRPC 的 C++ 异步编程_grpc 异步流模式
 - [原创首发]安全日志管理中心实战(3)——开源NIDS之suricata部署
 - 超详细手把手搭建在ubuntu系统的FFmpeg环境
 - Nginx运维之路(Docker多段构建新版本并增加第三方模
 - 92.1K小星星,一款开源免费的远程桌面,让你告别付费远程控制!
 - Go 人脸识别教程_piwigo人脸识别
 - 安卓手机安装Termux——搭建移动服务器
 - ubuntu 安装开发环境(c/c++ 15)_ubuntu安装c++编译器
 - Rust开发环境搭建指南:从安装到镜像配置的零坑实践
 - Windows系统安装VirtualBox构造本地Linux开发环境
 
 
- 标签列表
 - 
- cmd/c (90)
 - c++中::是什么意思 (84)
 - 标签用于 (71)
 - 主键只能有一个吗 (77)
 - c#console.writeline不显示 (95)
 - pythoncase语句 (88)
 - es6includes (74)
 - sqlset (76)
 - apt-getinstall-y (100)
 - node_modules怎么生成 (87)
 - chromepost (71)
 - flexdirection (73)
 - c++int转char (80)
 - mysqlany_value (79)
 - static函数和普通函数 (84)
 - el-date-picker开始日期早于结束日期 (76)
 - js判断是否是json字符串 (75)
 - c语言min函数头文件 (77)
 - asynccallback (87)
 - localstorage.removeitem (77)
 - vector线程安全吗 (73)
 - java (73)
 - js数组插入 (83)
 - mac安装java (72)
 - 无效的列索引 (74)
 
 
