1.ErrorFilter最高权限的配置
上一篇已经将所有的代码进行了展示,也说明了ErrorFilter存在的意义,为了使的ErrorFilter可以在所有的Filter之前执行,需要在WebConfig中设置其级别:
// 将ErrorFilter放置在最前边,如果在shiro抛出异常,比如token过期,为空等可以通过
// ErrorController 截取到抛出异常
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean bean =new FilterRegistrationBean<>();
// 设置Filter
bean.setFilter(new ErrorFilter());
bean.addUrlPatterns("/*");
// 最高权限
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
这样才能将其放置在所有filter最前边
2.对抛出异常进行处理
将ErrorFilter配置成最高权限后
try {
filterChain.doFilter(servletRequest, servletResponse);
}catch (Exception e) {
servletRequest.setAttribute(ERROR_URI, e);
// 将错误转派到ERROR_URL,使其控制器抛出异常,
// 切记:一定要在ShiroCgf中打开这个错误的url
servletRequest.getRequestDispatcher(ERROR_URI).forward(servletRequest, servletResponse);
}
通过try-catch捕获异常,一旦在shiro过程中出现异常,直接转发到ERROR_URL这个路径下,即为ErrorController中,ErrorController抛出异常,这样做的原因:由于我们自定义的异常都是基于controller的,但是shiro抛出异常的时候并非是在controller,如下:抛出的异常都是在TokenFilter中抛出的
HttpServletRequest request = (HttpServletRequest) servletRequest;
//从header中取出token
String token = request.getHeader(HEADER_TOKEN);
// 没有token
if (token ==null){
// 抛出异常
return JsonVos.raise(CodeMsg.NO_TOKEN);
}
// token过期
if (redisService ==null){
redisService = ApplicationContextUtils.getBean(UserInfoRedisService.class);
}
if (redisService.getUserByToken(token) ==null){
// 抛出异常
return JsonVos.raise(CodeMsg.TOKEN_EXPIRED);
}
3.正常代码逻辑
(1)URL拦截
由于在shiroCfg中设置了url的filter
// 设置URL拦截
Map urlMap =new LinkedHashMap<>();
// 用户登录
urlMap.put("/user/login","anon");
// 将错误的打开
urlMap.put(ErrorFilter.ERROR_URI,"anon");
// 其他添加需要token
urlMap.put("/**","token");
当输入的url需要进行拦截的话,在TokenFilter中的 isAccessAllowed 返回false 后调用isAccessAllowed,在isAccessAllowed中主要是进行token的验证是否正确,token是否过期
(2)鉴权,认证
然后调用进行鉴权,在TokenFilter 中isAccessAllowed 方法中调用login
// 鉴权(进入Realm)
// 这里调用login,并不是“登录”的意思,是为了触发Realm的相应方法去加载用户的角色、权限信息,以便鉴权
SecurityUtils.getSubject().login(new Token(token));
然后进入TokenRealm中调用函数doGetAuthenticationInfo
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException {
String tk = ((Token) token).getToken();
log.debug("TokenRealm - doGetAuthenticationInfo - {}", tk);
// // 这个地方会直接调用TokenMatcher中的方法
return new SimpleAuthenticationInfo(tk, tk, getName());
}
return new SimpleAuthenticationInfo(tk, tk, getName()) 调用进入TokenMatcher中进行密码验证,由于这里不需要进行密码的验证,所以在TokenMatcher的方法doCredentialsMatch中直接返回true,这样鉴权验证用户的过程就完了
(3)用户权限
当项目中出现一下
subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
@RequiresRoles(“admin”) :在方法上加注解的时候;
[@shiro.hasPermission name = “admin”][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。
@@RequiresPermissions("user:add") 在方法上的注解
就会调用TokenRealm中的doGetAuthorizationInfo 获取权限信息,根据获得的角色信息和权限信息进行请求或者是返回权限不足的结果
// 获取当前登录用户的token
String token = (String)principalCollection.getPrimaryPrincipal();
// 获取当前用户
SysUserDto user =redisService.getUserByToken(token);
SimpleAuthorizationInfo info =new SimpleAuthorizationInfo();
// info.addStringPermission("sysRole:allList");
List roles = user.getRoles();
if (CollectionUtils.isEmpty(roles))return info;
// 添加角色
for (SysRole role : roles) {
info.addRole(role.getName());
}
List resources = user.getResources();
if (CollectionUtils.isEmpty(resources))return info;
// 添加权限
for (SysResource resource : resources) {
info.addStringPermission(resource.getPermission());
}
return info;