本文讨论一个问题:
存储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);
|
思考
第一个问题,两种方式是否都能够实现需求功能?
我们需要实现的功能包括:
- 登录验证
- shiro中的权限验证
登录验证
对于”用户ID为key”的方式,需要前端传递用户id+token两个值,验证登录状态需要我们根据前端传递的用户ID,获取数据库中存储的token,与前端传递的token进行校验,如果一致,则校验通过,否则返回错误信息,提示用户需要重新登录等等。
对于“token为key”的方式,前端至少需要传递token一个值,根据前端传递的token,获取数据库中存储的用户ID,如果能获取到,则校验通过,否则提示用户token已过期,需要用户重新登录等等。
shiro中的权限验证
shiro中的权限验证,涉及到具体的实现机制,以token为key的方式,就以我们的真实实现为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
Subject s = SecurityUtils.getSubject(); JWTToken jwtToken = new JWTToken(token); subject.login(jwtToken);
import org.apache.shiro.authc.AuthenticationToken;
public class JWTToken implements AuthenticationToken { private static final long serialVersionUID = 1L;
private String token;
public JWTToken(String token) { this.token = token; }
@Override public Object getPrincipal() { return token; }
@Override public Object getCredentials() { return token; } }
public class ShiroDbRealm extends AuthorizingRealm{
@Override public boolean supports(AuthenticationToken token) { return token instanceof JWTToken; }
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); return simpleAuthorizationInfo; }
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { String token = (String) auth.getCredentials(); Integer userid = JwtUtils.getUserId(token); if (tuserid == null) { throw new AuthenticationException("token 校验失败"); } return new SimpleAuthenticationInfo(token, token, getName()); } }
|
如果采用userid为key的方式,不难实现,也修改其实现方式,
第二个问题,两种方式哪一种传输的数据量更少?
第一种方式需要前端每次请求都传递token+userid;而第二种实际上可以只传递token,后台根据token解密(或数据库查找)来获取用户信息。
第三个问题,两种方式哪种更安全?
两种方式的安全应该是一样的,核心是后台通过数据库保存token与userid的对应信息。
个人意见
个人比较细化第二种,以token为key的方式,首先,前端传递简单,只需要传递token即可;二是后端通过这种方式,可以统一当前登录人的获取方式,而不是每次在接口中获取header中的用户id。