前言:css
权限控制有 注解的方式,jsp shiro标签的方式,还有url 动态控制的方式。这里我使用最后一种方式来控制权限前端
思路:java
0.利用 PathMatchingFilter 拦截器web
1.根据用户名 来查询角色,redis
2.根据角色查询权限算法
3.获取请求的url spring
4判断 根据用户名查询的权限 是否包括 请求的urlapache
5.若是包括 则 放行,不包括重定向到 未受权界面缓存
package com.example.springboot.shiro.core.shiro.filter; import com.example.springboot.shiro.common.utils.SpringContextUtil; import com.example.springboot.shiro.core.shiro.token.TokenManager; import com.example.springboot.shiro.user.entity.Upermission; import com.example.springboot.shiro.user.service.LoginService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.PathMatchingFilter; import org.apache.shiro.web.util.WebUtils; import org.springframework.beans.factory.annotation.Autowired; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.util.List; /** * 权限 拦截策略 */ public class URLPathMatchingFilter extends PathMatchingFilter { @Autowired LoginService loginService; @Override protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { if (loginService==null){ loginService= SpringContextUtil.getContext().getBean(LoginService.class); } //请求的url String requestURL = getPathWithinApplication(request); System.out.println("请求的url :"+requestURL); Subject subject = SecurityUtils.getSubject(); if (!subject.isAuthenticated()){ // 若是没有登陆, 直接返回true 进入登陆流程 return true; } String email = TokenManager.getEmail(); List<Upermission> permissions = loginService. upermissions(email); boolean hasPermission = false; for (Upermission url : permissions) { if (url.getUrl().equals(requestURL)){ hasPermission = true; break; } } if (hasPermission){ return true; }else { UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径" + requestURL + "的权限"); subject.getSession().setAttribute("ex",ex); WebUtils.issueRedirect(request, response, "/unauthorized"); return false; } } }
配置 : ShiroConfiguration(shiro 配置类) springboot
package com.example.springboot.shiro.core.shiro.config; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import com.example.springboot.shiro.core.shiro.filter.KickoutSessionControlFilter; import com.example.springboot.shiro.core.shiro.filter.SessionControlInterceptor; import com.example.springboot.shiro.core.shiro.filter.SessionFilter; import com.example.springboot.shiro.core.shiro.filter.URLPathMatchingFilter; import com.example.springboot.shiro.core.shiro.token.SampleRealm; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.codec.Base64; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.crazycake.shiro.RedisManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.crazycake.shiro.RedisCacheManager; import org.springframework.data.redis.core.RedisTemplate; import javax.servlet.Filter; @Configuration //Shiro配置类 public class ShiroConfiguration { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; @Bean public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,由于在 * 初始化ShiroFilterFactoryBean的时候须要注入:SecurityManager * <p> * Filter Chain定义说明 * 一、一个URL能够配置多个Filter,使用逗号分隔 * 二、当设置多个过滤器时,所有验证经过,才视为经过 * 三、部分过滤器可指定参数,如perms,roles */ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { System.out.println("ShiroConfiguration.shirFilter()"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 若是不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登陆成功后要跳转的连接(没用,在js中跳转了) shiroFilterFactoryBean.setSuccessUrl("/index"); //未受权界面 shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); //拦截器. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //自定义拦截器 Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>(); //限制同一账号同时在线的个数。 filtersMap.put("kickout", kickoutSessionControlFilter()); //访问权限配置 filtersMap.put("requestURL", getURLPathMatchingFilter()); shiroFilterFactoryBean.setFilters(filtersMap); /* 配置映射关系*/ //authc:全部url都必须认证经过才能够访问; anon:全部url都均可以匿名访问 filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/index", "authc"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/updateSelf", "authc"); filterChainDefinitionMap.put("/updatePswd", "authc"); filterChainDefinitionMap.put("/mypermission", "authc"); filterChainDefinitionMap.put("/kickout", "anon"); filterChainDefinitionMap.put("/list", "authc"); filterChainDefinitionMap.put("/online", "authc"); filterChainDefinitionMap.put("/role", "authc"); filterChainDefinitionMap.put("/Roleassignment", "authc"); filterChainDefinitionMap.put("/permissionlist", "authc"); filterChainDefinitionMap.put("/PermissionAssignment", "authc"); /*加入自定义过滤器*/ filterChainDefinitionMap.put("/**", "kickout"); //下面的配置路径 都须要在上面配置 authc 不然访问不到filter filterChainDefinitionMap.put("/online","requestURL"); filterChainDefinitionMap.put("/list", "requestURL"); filterChainDefinitionMap.put("/role", "requestURL"); filterChainDefinitionMap.put("/Roleassignment", "requestURL"); filterChainDefinitionMap.put("/permissionlist", "requestURL"); filterChainDefinitionMap.put("/PermissionAssignment", "requestURL"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 访问 权限 拦截器 * @return */ public URLPathMatchingFilter getURLPathMatchingFilter() { return new URLPathMatchingFilter(); } /** * 自定义域 * * @return */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置realm. securityManager.setRealm(getDatabaseRealm()); // 自定义缓存实现 使用redis securityManager.setCacheManager(cacheManager()); securityManager.setSessionManager(sessionManager()); //注入记住我管理器; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } /** * 受权&认证 * * @return */ @Bean public SampleRealm getDatabaseRealm() { SampleRealm myShiroRealm = new SampleRealm(); System.out.println("myShiroRealm"); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } /** * 凭证匹配器 * (因为咱们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 * 因此咱们须要修改下doGetAuthenticationInfo中的代码; * ) * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; // hashedCredentialsMatcher.setHashIterations(2);//散列的次数,好比散列两次,至关于 md5(md5("")); return hashedCredentialsMatcher; } /** * 开启shiro aop注解支持. * 使用代理方式;因此须要开启代码支持; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * cacheManager 缓存 redis实现 * 使用的是shiro-redis开源插件 * * @return */ public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } /** * 配置shiro redisManager * 使用的是shiro-redis开源插件 * * @return */ public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host); redisManager.setPort(port); redisManager.setExpire(1800);// 配置缓存过时时间 redisManager.setTimeout(timeout); // redisManager.setPassword(password); return redisManager; } /** * Session Manager * 使用的是shiro-redis开源插件 */ @Bean(name = "sessionManager") public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionDAO(redisSessionDAO()); return sessionManager; } /** * RedisSessionDAO shiro sessionDao层的实现 经过redis * 使用的是shiro-redis开源插件 */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } /** * cookie管理对象;记住我功能 * * @return */ public CookieRememberMeManager rememberMeManager() { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); //rememberMe cookie加密的密钥 建议每一个项目都不同 默认AES算法 密钥长度(128 256 512 位) cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag==")); return cookieRememberMeManager; } /** * cookie对象; * * @return */ public SimpleCookie rememberMeCookie() { //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); //<!-- 记住我cookie生效时间30天 ,单位秒;--> simpleCookie.setMaxAge(2592000); return simpleCookie; } /** * 限制同一帐号登陆同时登陆人数控制 * * @return */ public KickoutSessionControlFilter kickoutSessionControlFilter() { KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter(); //使用cacheManager获取相应的cache来缓存用户登陆的会话;用于保存用户—会话之间的关系的; //这里咱们仍是用以前shiro使用的redisManager()实现的cacheManager()缓存管理 //也能够从新另写一个,从新配置缓存时间之类的自定义缓存属性 kickoutSessionControlFilter.setCacheManager(cacheManager()); //用于根据会话ID,获取会话进行踢出操做的; kickoutSessionControlFilter.setSessionManager(sessionManager()); //是否踢出后来登陆的,默认是false;即后者登陆的用户踢出前者登陆的用户;踢出顺序。 kickoutSessionControlFilter.setKickoutAfter(false); //同一个用户最大的会话数,默认1;好比2的意思是同一个用户容许最多同时两我的登陆; kickoutSessionControlFilter.setMaxSession(1); //被踢出后重定向到的地址; kickoutSessionControlFilter.setKickoutUrl("kickout"); return kickoutSessionControlFilter; } }
未受权 异常捕获:
package com.example.springboot.shiro.common.exception; import org.apache.shiro.authz.UnauthorizedException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.servlet.ModelAndView; /** * 未受权异常 捕获 */ @ControllerAdvice public class DefaultExceptionHandler { @ExceptionHandler({UnauthorizedException.class}) @ResponseStatus(HttpStatus.UNAUTHORIZED) public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) { ModelAndView mv = new ModelAndView(); mv.addObject("ex", e); mv.setViewName("unauthorized"); return mv; } }