网站首页 > 技术文章 正文
这篇讲一下若依框架认证所用的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 互联网项目的进步--前后端是怎么分离的?
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)