userList = userMapper.selectUserByRoleId(roleId); // 删除当前角色关联的用户缓存信息,用户再次访问接口时会重新授权 ; isRemoveSession为true时删除Session -> 即强制用户退出 if ( !CollectionUtils.isEmpty( userList ) ) { for (User user : userList) { ShiroUtils.deleteCache(user.getUsername(), isRemoveSession); } } log.info("--------------- 动态修改用户权限成功! ---------------"); }}四、shiro中自定义角色、权限过滤器
1、自定义uri权限过滤器 zqPerms
@Slf4jpublic class MyPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter { @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String requestUrl = httpRequest.getServletPath(); log.info("请求的url: " + requestUrl); // 检查是否拥有访问权限 Subject subject = this.getSubject(request, response); if (subject.getPrincipal() == null) { this.saveRequestAndRedirectToLogin(request, response); } else { // 转换成http的请求和响应 HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; // 获取请求头的值 String header = req.getHeader("X-Requested-With"); // ajax 的请求头里有X-Requested-With: XMLHttpRequest 正常请求没有 if (header!=null && "XMLHttpRequest".equals(header)){ resp.setContentType("text/json,charset=UTF-8"); resp.getWriter().print("{\"success\":false,\"msg\":\"没有权限操作!\"}"); }else { //正常请求 String unauthorizedUrl = this.getUnauthorizedUrl(); if (StringUtils.hasText(unauthorizedUrl)) { WebUtils.issueRedirect(request, response, unauthorizedUrl); } else { WebUtils.toHttp(response).sendError(401); } } } return false; } }
2、自定义角色权限过滤器 zqRoles
shiro原生的角色过滤器RolesAuthorizationFilter 默认是必须同时满足roles[admin,guest]才有权限,而自定义的zqRoles 只满足其中一个即可访问
ex: zqRoles[admin,guest]
public class MyRolesAuthorizationFilter extends AuthorizationFilter { @Override protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception { Subject subject = getSubject(req, resp); String[] rolesArray = (String[]) mappedValue; // 没有角色限制,有权限访问 if (rolesArray == null || rolesArray.length == 0) { return true; } for (int i = 0; i < rolesArray.length; i++) { //若当前用户是rolesArray中的任何一个,则有权限访问 if (subject.hasRole(rolesArray[i])) { return true; } } return false; }}
3、自定义token过滤器 token -> 判断token是否过期失效等
@Slf4jpublic class TokenCheckFilter extends UserFilter { /** * token过期、失效 */ private static final String TOKEN_EXPIRED_URL = "/api/auth/tokenExpired"; /** * 判断是否拥有权限 true:认证成功 false:认证失败 * mappedValue 访问该url时需要的权限 * subject.isPermitted 判断访问的用户是否拥有mappedValue权限 */ @Override public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; // 根据请求头拿到token String token = WebUtils.toHttp(request).getHeader(Constants.REQUEST_HEADER); log.info("浏览器token:" + token ); User userInfo = ShiroUtils.getUserInfo(); String userToken = userInfo.getToken(); // 检查token是否过期 if ( !token.equals(userToken) ){ return false; } return true; } /** * 认证失败回调的方法: 如果登录实体为null就保存请求和跳转登录页面,否则就跳转无权限配置页面 */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException { User userInfo = ShiroUtils.getUserInfo(); // 重定向错误提示处理 - 前后端分离情况下 WebUtils.issueRedirect(request, response, TOKEN_EXPIRED_URL); return false; }}
五、项目中会用到的一些工具类、常量等
温馨小提示:这里只是部分,详情可参考文章末尾给出的案例demo源码
1、Shiro工具类
public class ShiroUtils { /** 私有构造器 **/ private ShiroUtils(){ } private static RedisSessionDAO redisSessionDAO = SpringUtil.getBean(RedisSessionDAO.class); /** * 获取当前用户Session * @Return SysUserEntity 用户信息 */ public static Session getSession() { return SecurityUtils.getSubject().getSession(); } /** * 用户登出 */ public static void logout() { SecurityUtils.getSubject().logout(); } /** * 获取当前用户信息 * @Return SysUserEntity 用户信息 */ public static User getUserInfo() { return (User) SecurityUtils.getSubject().getPrincipal(); } /** * 删除用户缓存信息 * @Param username 用户名称 * @Param isRemoveSession 是否删除Session,删除后用户需重新登录 */ public static void deleteCache(String username, boolean isRemoveSession){ //从缓存中获取Session Session session = null; // 获取当前已登录的用户session列表 Collection sessions = redisSessionDAO.getActiveSessions(); User sysUserEntity; Object attribute = null; // 遍历Session,找到该用户名称对应的Session for(Session sessionInfo : sessions){ attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); if (attribute == null) { continue; } sysUserEntity = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal(); if (sysUserEntity == null) { continue; } if (Objects.equals(sysUserEntity.getUsername(), username)) { session=sessionInfo; // 清除该用户以前登录时保存的session,强制退出 -> 单用户登录处理 if (isRemoveSession) { redisSessionDAO.delete(session); } } } if (session == null||attribute == null) { return; } //删除session if (isRemoveSession) { redisSessionDAO.delete(session); } //删除Cache,再访问受限接口时会重新授权 DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager(); Authenticator authc = securityManager.getAuthenticator(); ((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute); } /** * 从缓存中获取指定用户名的Session * @param username */ private static Session getSessionByUsername(String username){ // 获取当前已登录的用户session列表 Collection sessions = redisSessionDAO.getActiveSessions(); User user; Object attribute; // 遍历Session,找到该用户名称对应的Session for(Session session : sessions){ attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); if (attribute == null) { continue; } user = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal(); if (user == null) { continue; } if (Objects.equals(user.getUsername(), username)) { return session; } } return null; }}
2、Redis常量类
public interface RedisConstant { /** * TOKEN前缀 */ String REDIS_PREFIX_LOGIN = "code-generator_token_%s";}
3、Spring上下文工具类
@Componentpublic class SpringUtil implements ApplicationContextAware { private static ApplicationContext context; /** * Spring在bean初始化后会判断是不是ApplicationContextAware的子类 * 如果该类是,setApplicationContext()方法,会将容器中ApplicationContext作为参数传入进去 */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } /** * 通过Name返回指定的Bean */ public static T getBean(Class beanClass) { return context.getBean(beanClass); }}
关于SpringBoot中怎么利用Shiro动态加载权限就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
分享题目:SpringBoot中怎么利用Shiro动态加载权限
分享网址:http://cdweb.net/article/jsehsp.html