用户尝试登录: 用户提供用户名和密码(或其他身份信息)尝试登录应用程序。
AuthenticatingFilter 的 createToken 方法: 当用户提交登录请求时,与登录相关的过滤器,比如你的 Oauth2Filter 中的 createToken 方法被调用。在这个方法中,创建了 AuthenticationToken 对象(这里是 Oauth2Token),并将其返回。
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
//获取请求token
String token = getRequestToken((HttpServletRequest) request);
if (StringUtils.isBlank(token)) {
return null;
}
return new Oauth2Token(token);
}
public class Oauth2Token implements AuthenticationToken {
/**
*
*/
private static final long serialVersionUID = 1L;
private String token;
public Oauth2Token(String token) {
this.token = token;
}
@Override
public String getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
SecurityManager 调用 Realm 的 doGetAuthenticationInfo 方法: Shiro 的 SecurityManager 接收到 AuthenticationToken 对象后,会调用配置的 Realm 的 doGetAuthenticationInfo 方法,传递 AuthenticationToken 给 Realm。这时,Oauth2Realm 中的 doGetAuthenticationInfo 方法被调用。
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String accessToken = (String) token.getPrincipal();
String userId = null;
try {
userId = jwtUtil.getUserId(accessToken);
} catch (Exception ex) {
throw new AuthenticationException("Token失效,请重新登录!");
}
//查询用户信息
SysUserEntity user = shiroService.queryUser(userId);
if (user != null) {
//账号锁定
if (user.getStatus() == 0) {
throw new LockedAccountException("账号已被锁定,请联系管理员");
}
}
return new SimpleAuthenticationInfo(user, accessToken, getName());
}
Realm 中执行身份认证逻辑: 在 Oauth2Realm 的 doGetAuthenticationInfo 方法中,你可以编写自定义的身份认证逻辑,例如验证用户名和密码是否匹配,检查用户是否被锁定等。
返回 AuthenticationInfo 对象: 在 doGetAuthenticationInfo 方法中,你需要创建并返回一个 AuthenticationInfo 对象,其中包含了用户的身份信息。通常,你会返回一个 SimpleAuthenticationInfo 对象,其中包括认证成功的用户对象以及用于后续身份验证的凭证信息(例如密码或令牌)。
Shiro 进行身份验证: Shiro 将获得的 AuthenticationInfo 与用户提供的身份信息进行比对,如果匹配,则认为用户通过了身份验证。
方法调用顺序 :
Oauth2Filter.isAccessAllowed (请求方法类型 是否为 OPTION) 如果返回true,则跳过token 验证,执行api。 如果返回false,则需要验证token。
Oauth2Filter.onAccessDenied (如果token 为空,直接返回401 报错 。) 如果token 不为空,执行executeLogin 方法,其中会调用Oauth2Filter.createToken,然后调用subject.login方法,其中会调用Oauth2Realm.doGetAuthenticationInfo 。
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
log.info("step : Oauth2Filter.isAccessAllowed");
// 请求方法类型是否为OPTION,如果是,则跳过token的解析校验
return ((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name());
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
log.info("step : Oauth2Filter.onAccessDenied");
//获取请求token,如果token不存在,直接返回401
String token = getRequestToken((HttpServletRequest) request);
if (StringUtils.isBlank(token)) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
String json = new Gson().toJson(RestResponse.error(401, "invalid token"));
httpResponse.getWriter().print(json);
return false;
}
// 调用 createToken 和 Oauth2Realm的doGetAuthenticationInfo
Boolean flag = executeLogin(request, response);
return flag;
}
ShiroConfig
配置哪些url 不需要经过验证
@Bean("shiroFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
//oauth过滤
Map<String, Filter> filters = new HashMap<>(16);
filters.put("oauth2", new Oauth2Filter());
shiroFilter.setFilters(filters);
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/sys/login", "anon");
filterMap.put("/sys/tyyw/login", "anon");
filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}