springboot + shiro 权限管理
springboot shiro 权限管理
版本 :
- springboot 2.3.4-RELEASE
- shiro 1.6.0
注: 此版本基于 ssm 与 shiro 下的权限管理_XGLLHZ的博客-CSDN博客 文章(有些小问题,目前未修改),关联阅读便于理解。
核心组件 :
- Subject 一般指用户
- SecurityManager 安全管理器,管理所有 subject
- SessionManager 会话管理器,管理用户登录后的 session
- Authentication 认证
- Authorization 鉴权
- CacheManager 缓存管理器,管理缓存(缓存可自定义)
流程图 :
TokenFilter :
// token filter(请求先进入此拦截器)
public class TokenFilter extends AbstractFilter {private static final Logger logger = LogManager.getLogger(TokenFilter.class);private final SYSUserService userService;public TokenFilter(SYSUserService userService) {this.userService = userService;}/*** 此拦截器工作流程:* 1、判断是否携带 token* 2、判断 token 是否过期* 3、判断用户登录是否过期(即 session 中是否存在用户信息)* 4、在 token 未过期且用户登录过期的情况刷新用户 session(系统内部自动登录)** 此处做自动登录的目的:* 在 spring-shiro 中,用户登录信息是放在 session 中的,* 而 session 默认有效时间为半小时,也就是说默认情况下,* 用户每隔半小时就得登录一次,针对这个问题有两种解决方法:* 1、session 有效时长设置为无限长(缺点是当用户数量过大时会严重占用系统内存)* 2、利用 token 机制结合 session(有效时长可设两小时),当 token 有效 session* 失效时,刷新 session 中的用户信息,当 token 过期时提示用户重新登录* * @param servletRequest* @param servletResponse* @return* @throws IOException*/@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain) throws ServletException, IOException {logger.info("enter TokenFilter.doFilter() params = {}", servletRequest.toString());HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;response.setContentType("application/json; charset=utf-8");String token = request.getHeader("token");// 若 token 为空则说明该请求为非法请求或不需要权限的请求if (StringUtils.isNotBlank(token)) {// 若 token 过期则返回响应体if (TokenUtil.checkToken(token) == 1) {ObjectMapper mapper = new ObjectMapper();PrintWriter writer = response.getWriter();writer.write(mapper.writeValueAsString(new APIResponse<>(ConstConfig.RE_LOGIN_EXPIRE_CODE,ConstConfig.RE_LOGIN_EXPIRE_MSG)));writer.flush();writer.close();return;}// 若 token 未过期则判断内存中是否有此用户身份验证信息Object object = null;try {object = SecurityUtils.getSubject().getPrincipal();} catch (Exception e) {logger.error("The userInfo is overdue in session!");}// 若没有此用户身份验证信息则根据 token 获取用户信息并调用 shiro 中的 subject.login()// 类似于系统内部自动登录(刷新 session 中的用户信息)if (object == null) {String username = userService.getUsernameByToken(token);if (StringUtils.isBlank(username)) {ObjectMapper mapper = new ObjectMapper();PrintWriter writer = response.getWriter();writer.write(mapper.writeValueAsString(new APIResponse<>(ConstConfig.RE_CHECK_TOKEN_ERROR_CODE,ConstConfig.RE_CHECK_TOKEN_ERROR_MSG)));writer.flush();writer.close();return;}SYSUserPo userPo = userService.getUserByUsername(username);if (userPo == null) {ObjectMapper mapper = new ObjectMapper();PrintWriter writer = response.getWriter();writer.write(mapper.writeValueAsString(new APIResponse<>(ConstConfig.SERVER_EXCEPTION_CODE,"User data does not exist")));writer.flush();writer.close();return;}// 调用 shiro 中的登录方法UsernamePasswordToken usernamePasswordToken =new UsernamePasswordToken(userPo.getUsername(), userPo.getPassword());Subject subject = SecurityUtils.getSubject();subject.login(usernamePasswordToken);}}filterChain.doFilter(servletRequest, servletResponse);}
}
ShiroRealm :
// 重写 shiro 父类中鉴权和认证的方法,以实现具体逻辑
public class ShiroRealm extends AuthorizingRealm {private static final Logger logger = LogManager.getLogger(ShiroRealm.class);private final SYSUserService userService;public ShiroRealm(SYSUserService userService) {this.userService = userService;}/*** 鉴权* 获取权限校验需要的信息(当前登录用户所具有的权限信息:权限、角色)* 调用时间: 1、默认情况下是每次请求资源(url)时都会调用;* 2、但可以将用户权限(资源)信息存放到缓存中,存放方式是在 subject.login()* 之后调用 subject.isPermitted("test")方法,在这个方法内部会调用下面的* 方法来获取用户权限(资源)信息,同时需要在 shiro 配置文件中配置相应的缓存管理器;* 3、放入缓存之后只在每次登录时调用;* 检验方式: shiro 的权限校验方式有两种,即基于权限(资源)、基于角色* 此项目采用基于权限(资源)的方式* @param principalCollection* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {logger.info("enter ShiroRealm.doGetAuthorizationInfo() authority");// 获取登录成功的用户名// 这里会从 session 中获取(登录执行 subject.login 方法时会将用户信息放入 session)String username = (String) principalCollection.fromRealm(getName()).iterator().next();// 根据用户名获取该用户所具有的权限列表SYSUserPo sysUserPo = new SYSUserPo();sysUserPo.setUsername(username);List<SYSPermPo> permList = userService.listPermByUser(sysUserPo);List<String> perms = null;if (permList != null && permList.size() != 0) {perms = permList.stream().map(SYSPermPo::getPermUrl).collect(Collectors.toList());}SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addStringPermissions(perms);return info;}/*** 认证* 获取身份验证需要的信息(数据库中的用户数据)* 调用时间: 登录时调用,即请求 /admin/user/login时(service 中 的 subject.login())* @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {logger.info("enter ShiroRealm.doGetAuthenticationInfo() authenticate");// 用户登录提交的信息UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();if (StringUtils.isBlank(username)) {throw new GlobalException(ConstConfig.RE_NAME_OR_PASSWORD_ERROR_CODE,ConstConfig.RE_NAME_OR_PASSWORD_ERROR_MSG);}// 根据用户名查询到的信息SYSUserPo userPo = userService.getUserByUsername(username);if (userPo == null) {throw new GlobalException(ConstConfig.SERVER_EXCEPTION_CODE, "User data does not exist");}return new SimpleAuthenticationInfo(userPo.getUsername(), userPo.getPassword(), getName());}
}
ShiroConfig :
// shiro config
@Component
public class ShiroConfig {public static final String PERMISSION_STRING = "perms[\"{0}\"]";private final SYSPermMapper permMapper;private final SYSUserService userService;public ShiroConfig(SYSPermMapper permMapper, SYSUserService userService) {this.permMapper = permMapper;this.userService = userService;}/*** 自定义实现 ShiroFilterFactoryBean** 关于 shiro 中的 filterChains(过滤链)* * 1、map 中的 key 表示要拦截的请求 url,value 表示处理此 url 所使用的拦截器,* 其中 value 的值可以是 shiro 提供的拦截器也可以是自定义拦截器* * 2、value 的值可以为多个(以逗号隔开),表示该 url(key) 要被多个拦截器处理,* 其中执行顺序为 value 中的顺序,如: filterChains.put("/admin/user/list", "tokenFilter,authc")* 表示 /admin/user/list 请求会依次经过 tokenFilter(自定义)、authc 拦截器** @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();factoryBean.setSecurityManager(securityManager);// loginUrl 在前后端分离下,此处应为返回登录提示的接口 api// 即,当系统发现当前请求地址需要授权才可访问时返回登录提示factoryBean.setLoginUrl("/admin/user/login_code");// unAuthorizedUrl 在前后端分离下,此处应为鉴权失败后返回权限不足的接口 api// 即,当鉴权失败后返回权限不足提示factoryBean.setUnauthorizedUrl("/admin/user/authorizingFail");// 自定义拦截器 token 校验Map<String, Filter> filters = new LinkedHashMap<>();filters.put("tokenFilter", new TokenFilter(userService));factoryBean.setFilters(filters);Map<String, String> filterChains = new LinkedHashMap<>(); // 承载过滤链的变量// 从数据库中获取权限(资源)url 及其对应的 roleList// 将存在 roleList 的 url 加入到过滤链中,因为 sys_perm 表中放的是所有资源的 url,// 其中有些资源不需要权限就可以访问,所以不需要放到过滤链中// 凡是放到过滤链中的 url 都会被拦截List<SYSPermPo> list = permMapper.allUrlRole();if (list != null && list.size() != 0) {list.stream().peek(a -> {if (StringUtils.isNoneBlank(a.getPermUrl()) && !a.getRoleList().isEmpty()) {filterChains.put(a.getPermUrl(), "tokenFilter,"+ MessageFormat.format(PERMISSION_STRING, a.getPermUrl()));}}).collect(Collectors.toList());}factoryBean.setFilterChainDefinitionMap(filterChains);return factoryBean;}@Beanpublic SecurityManager securityManager(ShiroRealm shiroRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(shiroRealm);return securityManager;}@Beanpublic ShiroRealm shiroRealm() {return new ShiroRealm(userService);}
}
shiro filter :
标识 | 名称 | 优先级 | 说明 | 对应类 |
---|---|---|---|---|
anon | 匿名拦截器 | 1 | 不需要登录即可访问,一般用于静态资源 | AnonymousFilter |
authc | 登录拦截器 | 2 | 需要登陆才可访问 | FormAuthenticationFilter |
authcBasicHttp | Http拦截器 | 3 | Http 拦截器 非常用类型 | BasicAuthenticationFilter |
logout | 登出拦截器 | 4 | 用户登出拦截器 主要属性 redirectUrl 登出后重定向地址 | LogoutFilter |
noSessionCreation | 不创建会话拦截器 | 5 | 没用过 | NoSessionCreationFilter |
perms | 权限拦截器 | 6 | 验证用户是否有权限访问资源 | PermissionAuthenticationFilter |
port | 端口拦截器 | 7 | 拦截端口,针对指定端口做出重定向 | PortFilter |
rest | rest 风格拦截器 | 8 | 根据 rest 风格 url 构建权限 非常用 | HttpMethodPermissionFilter |
roles | 角色拦截器 | 9 | 验证用户是否有角色访问资源 | RolesAuthorizationFilter |
ssl | ssl 拦截器 | 10 | 拦截非 https 请求,并跳转到 443 | SslFilter |
user | 用户拦截器 | 11 | 用户认证通过或开启记住我功能 | SslFilter |
RefreshFilterChains :
// shiro 过滤链是在服务启动时从数据库加载到内存中的
// 则当数据库权限数据发生变化时需要刷新内存中的过滤链
// 在添加修改角色权限相关方法中使用 refreshFilterChains.refreshFilterChains() 刷新过滤链
@Component
public class RefreshFilterChains {private static final Logger logger = LogManager.getLogger(RefreshFilterChains.class);public static final String PERMISSION_STRING = "perms[\"{0}\"]";private final ShiroFilterFactoryBean shiroFilterFactoryBean;private final SYSPermMapper permMapper;public RefreshFilterChains(ShiroFilterFactoryBean shiroFilterFactoryBean, SYSPermMapper permMapper) {this.shiroFilterFactoryBean = shiroFilterFactoryBean;this.permMapper = permMapper;}/*** 刷新过滤链(线程安全)*/public void refreshFilterChains() {logger.info("refresh shiro filter chains");synchronized (shiroFilterFactoryBean) {AbstractShiroFilter filter;try {// 获取 shiro 拦截器实例filter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();// 获取路径匹配过滤链解析器实例PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) filter.getFilterChainResolver();// 获取默认过滤链管理器DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager();// 清除过滤链manager.getFilterChains().clear();shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();Map<String, String> filterChains = new LinkedHashMap<>();List<SYSPermPo> list = permMapper.allUrlRole();if (list != null && list.size() != 0) {list.stream().peek(a -> {if (StringUtils.isNoneBlank(a.getPermUrl()) && !a.getRoleList().isEmpty()) {filterChains.put(a.getPermUrl(), "tokenFilter,"+ MessageFormat.format(PERMISSION_STRING, a.getPermUrl()));}}).collect(Collectors.toList());}shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChains);// 重新生成过滤链Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();if (!CollectionUtils.isEmpty(filterChainDefinitionMap)) {filterChains.forEach((key, value) -> {manager.createChain(key, value.replace(" ", ""));});}logger.info("The refreshed filter chains is {}", filterChainDefinitionMap.toString());} catch (Exception e) {logger.error("Failed refresh shiro filter chains");e.printStackTrace();}}}
}
完结 撒花 庆祝
人生何处不相逢.mp3-娴公主
springboot + shiro 权限管理
springboot shiro 权限管理
版本 :
- springboot 2.3.4-RELEASE
- shiro 1.6.0
注: 此版本基于 ssm 与 shiro 下的权限管理_XGLLHZ的博客-CSDN博客 文章(有些小问题,目前未修改),关联阅读便于理解。
核心组件 :
- Subject 一般指用户
- SecurityManager 安全管理器,管理所有 subject
- SessionManager 会话管理器,管理用户登录后的 session
- Authentication 认证
- Authorization 鉴权
- CacheManager 缓存管理器,管理缓存(缓存可自定义)
流程图 :
TokenFilter :
// token filter(请求先进入此拦截器)
public class TokenFilter extends AbstractFilter {private static final Logger logger = LogManager.getLogger(TokenFilter.class);private final SYSUserService userService;public TokenFilter(SYSUserService userService) {this.userService = userService;}/*** 此拦截器工作流程:* 1、判断是否携带 token* 2、判断 token 是否过期* 3、判断用户登录是否过期(即 session 中是否存在用户信息)* 4、在 token 未过期且用户登录过期的情况刷新用户 session(系统内部自动登录)** 此处做自动登录的目的:* 在 spring-shiro 中,用户登录信息是放在 session 中的,* 而 session 默认有效时间为半小时,也就是说默认情况下,* 用户每隔半小时就得登录一次,针对这个问题有两种解决方法:* 1、session 有效时长设置为无限长(缺点是当用户数量过大时会严重占用系统内存)* 2、利用 token 机制结合 session(有效时长可设两小时),当 token 有效 session* 失效时,刷新 session 中的用户信息,当 token 过期时提示用户重新登录* * @param servletRequest* @param servletResponse* @return* @throws IOException*/@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain) throws ServletException, IOException {logger.info("enter TokenFilter.doFilter() params = {}", servletRequest.toString());HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;response.setContentType("application/json; charset=utf-8");String token = request.getHeader("token");// 若 token 为空则说明该请求为非法请求或不需要权限的请求if (StringUtils.isNotBlank(token)) {// 若 token 过期则返回响应体if (TokenUtil.checkToken(token) == 1) {ObjectMapper mapper = new ObjectMapper();PrintWriter writer = response.getWriter();writer.write(mapper.writeValueAsString(new APIResponse<>(ConstConfig.RE_LOGIN_EXPIRE_CODE,ConstConfig.RE_LOGIN_EXPIRE_MSG)));writer.flush();writer.close();return;}// 若 token 未过期则判断内存中是否有此用户身份验证信息Object object = null;try {object = SecurityUtils.getSubject().getPrincipal();} catch (Exception e) {logger.error("The userInfo is overdue in session!");}// 若没有此用户身份验证信息则根据 token 获取用户信息并调用 shiro 中的 subject.login()// 类似于系统内部自动登录(刷新 session 中的用户信息)if (object == null) {String username = userService.getUsernameByToken(token);if (StringUtils.isBlank(username)) {ObjectMapper mapper = new ObjectMapper();PrintWriter writer = response.getWriter();writer.write(mapper.writeValueAsString(new APIResponse<>(ConstConfig.RE_CHECK_TOKEN_ERROR_CODE,ConstConfig.RE_CHECK_TOKEN_ERROR_MSG)));writer.flush();writer.close();return;}SYSUserPo userPo = userService.getUserByUsername(username);if (userPo == null) {ObjectMapper mapper = new ObjectMapper();PrintWriter writer = response.getWriter();writer.write(mapper.writeValueAsString(new APIResponse<>(ConstConfig.SERVER_EXCEPTION_CODE,"User data does not exist")));writer.flush();writer.close();return;}// 调用 shiro 中的登录方法UsernamePasswordToken usernamePasswordToken =new UsernamePasswordToken(userPo.getUsername(), userPo.getPassword());Subject subject = SecurityUtils.getSubject();subject.login(usernamePasswordToken);}}filterChain.doFilter(servletRequest, servletResponse);}
}
ShiroRealm :
// 重写 shiro 父类中鉴权和认证的方法,以实现具体逻辑
public class ShiroRealm extends AuthorizingRealm {private static final Logger logger = LogManager.getLogger(ShiroRealm.class);private final SYSUserService userService;public ShiroRealm(SYSUserService userService) {this.userService = userService;}/*** 鉴权* 获取权限校验需要的信息(当前登录用户所具有的权限信息:权限、角色)* 调用时间: 1、默认情况下是每次请求资源(url)时都会调用;* 2、但可以将用户权限(资源)信息存放到缓存中,存放方式是在 subject.login()* 之后调用 subject.isPermitted("test")方法,在这个方法内部会调用下面的* 方法来获取用户权限(资源)信息,同时需要在 shiro 配置文件中配置相应的缓存管理器;* 3、放入缓存之后只在每次登录时调用;* 检验方式: shiro 的权限校验方式有两种,即基于权限(资源)、基于角色* 此项目采用基于权限(资源)的方式* @param principalCollection* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {logger.info("enter ShiroRealm.doGetAuthorizationInfo() authority");// 获取登录成功的用户名// 这里会从 session 中获取(登录执行 subject.login 方法时会将用户信息放入 session)String username = (String) principalCollection.fromRealm(getName()).iterator().next();// 根据用户名获取该用户所具有的权限列表SYSUserPo sysUserPo = new SYSUserPo();sysUserPo.setUsername(username);List<SYSPermPo> permList = userService.listPermByUser(sysUserPo);List<String> perms = null;if (permList != null && permList.size() != 0) {perms = permList.stream().map(SYSPermPo::getPermUrl).collect(Collectors.toList());}SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addStringPermissions(perms);return info;}/*** 认证* 获取身份验证需要的信息(数据库中的用户数据)* 调用时间: 登录时调用,即请求 /admin/user/login时(service 中 的 subject.login())* @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {logger.info("enter ShiroRealm.doGetAuthenticationInfo() authenticate");// 用户登录提交的信息UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();if (StringUtils.isBlank(username)) {throw new GlobalException(ConstConfig.RE_NAME_OR_PASSWORD_ERROR_CODE,ConstConfig.RE_NAME_OR_PASSWORD_ERROR_MSG);}// 根据用户名查询到的信息SYSUserPo userPo = userService.getUserByUsername(username);if (userPo == null) {throw new GlobalException(ConstConfig.SERVER_EXCEPTION_CODE, "User data does not exist");}return new SimpleAuthenticationInfo(userPo.getUsername(), userPo.getPassword(), getName());}
}
ShiroConfig :
// shiro config
@Component
public class ShiroConfig {public static final String PERMISSION_STRING = "perms[\"{0}\"]";private final SYSPermMapper permMapper;private final SYSUserService userService;public ShiroConfig(SYSPermMapper permMapper, SYSUserService userService) {this.permMapper = permMapper;this.userService = userService;}/*** 自定义实现 ShiroFilterFactoryBean** 关于 shiro 中的 filterChains(过滤链)* * 1、map 中的 key 表示要拦截的请求 url,value 表示处理此 url 所使用的拦截器,* 其中 value 的值可以是 shiro 提供的拦截器也可以是自定义拦截器* * 2、value 的值可以为多个(以逗号隔开),表示该 url(key) 要被多个拦截器处理,* 其中执行顺序为 value 中的顺序,如: filterChains.put("/admin/user/list", "tokenFilter,authc")* 表示 /admin/user/list 请求会依次经过 tokenFilter(自定义)、authc 拦截器** @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();factoryBean.setSecurityManager(securityManager);// loginUrl 在前后端分离下,此处应为返回登录提示的接口 api// 即,当系统发现当前请求地址需要授权才可访问时返回登录提示factoryBean.setLoginUrl("/admin/user/login_code");// unAuthorizedUrl 在前后端分离下,此处应为鉴权失败后返回权限不足的接口 api// 即,当鉴权失败后返回权限不足提示factoryBean.setUnauthorizedUrl("/admin/user/authorizingFail");// 自定义拦截器 token 校验Map<String, Filter> filters = new LinkedHashMap<>();filters.put("tokenFilter", new TokenFilter(userService));factoryBean.setFilters(filters);Map<String, String> filterChains = new LinkedHashMap<>(); // 承载过滤链的变量// 从数据库中获取权限(资源)url 及其对应的 roleList// 将存在 roleList 的 url 加入到过滤链中,因为 sys_perm 表中放的是所有资源的 url,// 其中有些资源不需要权限就可以访问,所以不需要放到过滤链中// 凡是放到过滤链中的 url 都会被拦截List<SYSPermPo> list = permMapper.allUrlRole();if (list != null && list.size() != 0) {list.stream().peek(a -> {if (StringUtils.isNoneBlank(a.getPermUrl()) && !a.getRoleList().isEmpty()) {filterChains.put(a.getPermUrl(), "tokenFilter,"+ MessageFormat.format(PERMISSION_STRING, a.getPermUrl()));}}).collect(Collectors.toList());}factoryBean.setFilterChainDefinitionMap(filterChains);return factoryBean;}@Beanpublic SecurityManager securityManager(ShiroRealm shiroRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(shiroRealm);return securityManager;}@Beanpublic ShiroRealm shiroRealm() {return new ShiroRealm(userService);}
}
shiro filter :
标识 | 名称 | 优先级 | 说明 | 对应类 |
---|---|---|---|---|
anon | 匿名拦截器 | 1 | 不需要登录即可访问,一般用于静态资源 | AnonymousFilter |
authc | 登录拦截器 | 2 | 需要登陆才可访问 | FormAuthenticationFilter |
authcBasicHttp | Http拦截器 | 3 | Http 拦截器 非常用类型 | BasicAuthenticationFilter |
logout | 登出拦截器 | 4 | 用户登出拦截器 主要属性 redirectUrl 登出后重定向地址 | LogoutFilter |
noSessionCreation | 不创建会话拦截器 | 5 | 没用过 | NoSessionCreationFilter |
perms | 权限拦截器 | 6 | 验证用户是否有权限访问资源 | PermissionAuthenticationFilter |
port | 端口拦截器 | 7 | 拦截端口,针对指定端口做出重定向 | PortFilter |
rest | rest 风格拦截器 | 8 | 根据 rest 风格 url 构建权限 非常用 | HttpMethodPermissionFilter |
roles | 角色拦截器 | 9 | 验证用户是否有角色访问资源 | RolesAuthorizationFilter |
ssl | ssl 拦截器 | 10 | 拦截非 https 请求,并跳转到 443 | SslFilter |
user | 用户拦截器 | 11 | 用户认证通过或开启记住我功能 | SslFilter |
RefreshFilterChains :
// shiro 过滤链是在服务启动时从数据库加载到内存中的
// 则当数据库权限数据发生变化时需要刷新内存中的过滤链
// 在添加修改角色权限相关方法中使用 refreshFilterChains.refreshFilterChains() 刷新过滤链
@Component
public class RefreshFilterChains {private static final Logger logger = LogManager.getLogger(RefreshFilterChains.class);public static final String PERMISSION_STRING = "perms[\"{0}\"]";private final ShiroFilterFactoryBean shiroFilterFactoryBean;private final SYSPermMapper permMapper;public RefreshFilterChains(ShiroFilterFactoryBean shiroFilterFactoryBean, SYSPermMapper permMapper) {this.shiroFilterFactoryBean = shiroFilterFactoryBean;this.permMapper = permMapper;}/*** 刷新过滤链(线程安全)*/public void refreshFilterChains() {logger.info("refresh shiro filter chains");synchronized (shiroFilterFactoryBean) {AbstractShiroFilter filter;try {// 获取 shiro 拦截器实例filter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();// 获取路径匹配过滤链解析器实例PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) filter.getFilterChainResolver();// 获取默认过滤链管理器DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager();// 清除过滤链manager.getFilterChains().clear();shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();Map<String, String> filterChains = new LinkedHashMap<>();List<SYSPermPo> list = permMapper.allUrlRole();if (list != null && list.size() != 0) {list.stream().peek(a -> {if (StringUtils.isNoneBlank(a.getPermUrl()) && !a.getRoleList().isEmpty()) {filterChains.put(a.getPermUrl(), "tokenFilter,"+ MessageFormat.format(PERMISSION_STRING, a.getPermUrl()));}}).collect(Collectors.toList());}shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChains);// 重新生成过滤链Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();if (!CollectionUtils.isEmpty(filterChainDefinitionMap)) {filterChains.forEach((key, value) -> {manager.createChain(key, value.replace(" ", ""));});}logger.info("The refreshed filter chains is {}", filterChainDefinitionMap.toString());} catch (Exception e) {logger.error("Failed refresh shiro filter chains");e.printStackTrace();}}}
}
完结 撒花 庆祝
人生何处不相逢.mp3-娴公主
发布评论