讨论两种Redis中Token的存储方式

x33g5p2x  于2022-05-16 转载在 Redis  
字(2.3k)|赞(0)|评价(0)|浏览(540)

本文分享自华为云社区《讨论两种Redis中Token的存储方式》,作者: 洛叶飘 。

本文讨论一个问题:

存储token时,token与对应用户id谁来作为key?

问题起源

问题起源于要给公司的后台管理系统添加权限管理,选用的是开源框架shiro,而原本系统上是采用token做了登录校验的。

我所采用的shiro验证方式是,每次接口请求,根据token来获取用户id,然后通过shiro中的登录验证机制来进行权限校验。

因此,“根据token获取用户id”就要求在存储用户token时,以token为键值key,以用户ID为value值。

然而此时面临一个问题是,系统原本的token存储方式如下,我们称之为第一种:用户ID为key。

  1. cache.set(TOKEN_PREFIX + userid, token);

这就需要我做出判断,需不需要修改token的存储方式为下面的形式:我们称之为第二种:token为key。

  1. cache.set(TOKEN_PREFIX + token, userid);

思考

第一个问题,两种方式是否都能够实现需求功能?

我们需要实现的功能包括:

  1. 登录验证
  2. shiro中的权限验证

登录验证

对于"用户ID为key"的方式,需要前端传递用户id+token两个值,验证登录状态需要我们根据前端传递的用户ID,获取数据库中存储的token,与前端传递的token进行校验,如果一致,则校验通过,否则返回错误信息,提示用户需要重新登录等等。

对于“token为key”的方式,前端至少需要传递token一个值,根据前端传递的token,获取数据库中存储的用户ID,如果能获取到,则校验通过,否则提示用户token已过期,需要用户重新登录等等。

shiro中的权限验证

shiro中的权限验证,涉及到具体的实现机制,以token为key的方式,就以我们的真实实现为例:

  1. // shiro登录代码:
  2. Subject s = SecurityUtils.getSubject();
  3. JWTToken jwtToken = new JWTToken(token);
  4. subject.login(jwtToken);
  5. // 实现AuthenticationToken的类:
  6. import org.apache.shiro.authc.AuthenticationToken;
  7. public class JWTToken implements AuthenticationToken {
  8. private static final long serialVersionUID = 1L;
  9. // 密钥
  10. private String token;
  11. public JWTToken(String token) {
  12. this.token = token;
  13. }
  14. @Override
  15. public Object getPrincipal() {
  16. return token;
  17. }
  18. @Override
  19. public Object getCredentials() {
  20. return token;
  21. }
  22. }
  23. /**
  24. * 自定义的登录验证类:
  25. */
  26. public class ShiroDbRealm extends AuthorizingRealm{
  27. /**
  28. * 重写shiro的token
  29. */
  30. @Override
  31. public boolean supports(AuthenticationToken token) {
  32. return token instanceof JWTToken;
  33. }
  34. /**
  35. * 角色,权限认证
  36. */
  37. @Override
  38. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  39. SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
  40. //这里可以连接数据库根据用户账户进行查询用户角色权限等信息
  41. return simpleAuthorizationInfo;
  42. }
  43. /**
  44. * 自定义认证
  45. */
  46. @Override
  47. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
  48. String token = (String) auth.getCredentials();
  49. // 解密获得userid,用于和数据库进行对比
  50. // getUserId实际就是通过token,在数据库中取对应的userid
  51. Integer userid = JwtUtils.getUserId(token);
  52. if (tuserid == null) {
  53. throw new AuthenticationException("token 校验失败");
  54. }
  55. return new SimpleAuthenticationInfo(token, token, getName());
  56. }
  57. }

如果采用userid为key的方式,不难实现,也修改其实现方式,

第二个问题,两种方式哪一种传输的数据量更少?

第一种方式需要前端每次请求都传递token+userid;而第二种实际上可以只传递token,后台根据token解密(或数据库查找)来获取用户信息。

第三个问题,两种方式哪种更安全?

两种方式的安全应该是一样的,核心是后台通过数据库保存token与userid的对应信息。

个人意见

个人比较细化第二种,以token为key的方式,首先,前端传递简单,只需要传递token即可;二是后端通过这种方式,可以统一当前登录人的获取方式,而不是每次在接口中获取header中的用户id。

点击关注,第一时间了解华为云新鲜技术~​

相关文章

最新文章

更多