Shiro实现jwt验证流程梳理
由于业务需要用到jwt验证,需要把实现的用户密码验证方式修改为jwt的验证方式.
在把之前的登录流程修改为jwt验证时,是省略掉了shiro内部的login步骤和session管理
代码修改如下:
- Subject subject = SecurityUtils.getSubject();
- subject.login(usernamePasswordToken); //完成登录
- UserInfo2 user = (UserInfo2) subject.getPrincipal();
- session.setAttribute(Constant.CURRENT_USER, user);
+ if (userBean.getPasswrod().equals(loginInfo.getPassword())) {
+ String token = JWTUtil.sign(loginInfo.getLoginname(),loginInfo.getPassword());
+ SessionManager.addOrUpdateSessionEntity(token,SessionManager.USER_BEAN,userBean);
+ return new Result<String >(200,"success", token);
+ }
由于在controller省略掉了登录流程,需要添加Filter过滤,在Filter中调用login步骤,代码执行流程:
preHandle->isAccessAllowed->isLoginAttempt->executeLogin
login的登录调用流程如下
executeLogin
login
authenticate
onAuthenticate
UserRealm::getAuthenticationInfo
doGetAuthenticationInfo
真正的验证流程还是在UserRealm::doGetAuthenticationInfo
中
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
// 解密获得username,用于和数据库进行对比
String loginname = JWTUtil.getLoginname(token);
if (loginname == null) {
throw new AuthenticationException("token can not match !");
}
UserBean userBean = (UserBean) SessionManager.getSessionEntity(token,SessionManager.USER_BEAN);
if (userBean == null ) {
userBean = userService.getUserBean(loginname);
if (userBean == null) {
throw new AuthenticationException("user " + loginname + " does not exist");
} else {
SessionManager.addOrUpdateSessionEntity(token,SessionManager.USER_BEAN,userBean);
}
}
if (! JWTUtil.verify(token, loginname, userBean.getPasswrod())) {
throw new AuthenticationException("username or password is wrong");
}
return new SimpleAuthenticationInfo(token, token, "userRealm");
}
- 为什么需要在Filter中调用login,不能在loginUser中调用login?
由于Shiro默认的验证方式是基于session的,在基于token验证的方式中,不能依赖session做为登录的判断依据.
Shiro预定义的AuthenticationFilter::isAccessAllowed
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = getSubject(request, response);
return subject.isAuthenticated();
}
在JWTFilter中,如下
@Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
if (!isLoginAttempt(request, response) || !executeLogin(request,response)) {
response401(request,response);
}
} catch (UnauthorizedException e) {
response403(request,response);
return false;
} catch (AuthenticationException ae) {
response401(request, response);
return false;
}
return true;
}
其中isLoginAttempt时通过判断请求的httpHeader中是否包含Authentication
字段,在这里,自定义的字段取值为jwt token.