相關教程:html
1. springboot+shiro整合教程
java
2. springboot+shiro+redis(單機redis版)整合教程web
3. springboot+shiro+redis(集羣redis版)整合教程redis
參考此教程前請先閱讀 2.springboot+shiro+redis(單機redis版)整合教程,此教程是在其基礎上進行修改添加動態角色權限的。spring
本教程整合環境: java8 maven redis(單機)數據庫
開發工具: ideaapache
版本: springboot 1.5.15.RELEASEapi
注:緩存
1.本教程數據操做是模擬數據庫操做,並無真正進行持久化,自行修改便可。安全
項目結構,在 springboot+shiro+redis(單機redis版)整合教程 基礎上進行的添加和修改結構以下:
首先添加角色權限實體類RolesPermissions.java
package webapp.model; import lombok.Data; import java.io.Serializable; @Data public class RolesPermissions implements Serializable { private static final long serialVersionUID = 1L; /** * @備註: * @字段:id BIGINT(19) */ private Long id; /** * @備註:建立時間 * @字段:create_time DATETIME(19) */ private java.util.Date createTime; /** * @備註:更新時間 * @字段:update_time DATETIME(19) */ private java.util.Date updateTime; /** * @備註:角色id * @字段:roles_id BIGINT(19) */ private Long rolesId; /** * @備註:權限id * @字段:permissions_id BIGINT(19) */ private Long permissionsId; /** * @備註:角色名稱 * @字段:roles_name VARCHAR(100) */ private String rolesName; /** * @備註:api接口 * @字段:url VARCHAR(512) */ private String url; }
添加service層 RolesPermissionsService.java和 RolesPermissionsServiceImpl.java
RolesPermissionsService.java:
package webapp.service; import webapp.model.RolesPermissions; import java.util.List; public interface RolesPermissionsService { List<RolesPermissions> selectList(); List<RolesPermissions> selectByAuserName(String auserName); }
RolesPermissionsServiceImpl.java:
package webapp.service.impl; import org.springframework.stereotype.Service; import webapp.model.RolesPermissions; import webapp.service.RolesPermissionsService; import java.util.LinkedList; import java.util.List; @Service public class RolesPermissionsServiceImpl implements RolesPermissionsService { @Override public List<RolesPermissions> selectList() { List<RolesPermissions> rolesPermissionsList = new LinkedList<>(); RolesPermissions rolesPermissions = new RolesPermissions(); rolesPermissions.setId(1L); rolesPermissions.setPermissionsId(1L); rolesPermissions.setRolesId(1L); rolesPermissions.setRolesName("超級管理員"); rolesPermissions.setUrl("/index.html"); rolesPermissionsList.add(rolesPermissions); RolesPermissions rolesPermissions2 = new RolesPermissions(); rolesPermissions2.setId(2L); rolesPermissions2.setPermissionsId(1L); rolesPermissions2.setRolesId(2L); rolesPermissions2.setRolesName("管理員"); rolesPermissions2.setUrl("/test.html"); rolesPermissionsList.add(rolesPermissions2); RolesPermissions rolesPermissions3 = new RolesPermissions(); rolesPermissions3.setId(3L); rolesPermissions3.setPermissionsId(1L); rolesPermissions3.setRolesId(2L); rolesPermissions3.setRolesName("超級管理員"); rolesPermissions3.setUrl("/test.html"); rolesPermissionsList.add(rolesPermissions3); RolesPermissions rolesPermissions4 = new RolesPermissions(); rolesPermissions4.setId(4L); rolesPermissions4.setPermissionsId(3L); rolesPermissions4.setRolesId(1L); rolesPermissions4.setRolesName("超級管理員"); rolesPermissions4.setUrl("/core/user/findUser"); rolesPermissionsList.add(rolesPermissions4); return rolesPermissionsList; } @Override public List<RolesPermissions> selectByAuserName(String auserName) { List<RolesPermissions> rolesPermissionsList = new LinkedList<>(); RolesPermissions rolesPermissions = new RolesPermissions(); rolesPermissions.setId(1L); rolesPermissions.setPermissionsId(1L); rolesPermissions.setRolesId(1L); rolesPermissions.setRolesName("運營管理員"); rolesPermissions.setUrl("/index.html"); rolesPermissionsList.add(rolesPermissions); return rolesPermissionsList; } }
修改登陸相關接口 UserController.java:
package webapp.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; import webapp.service.UserService; import javax.annotation.Resource; import java.io.Serializable; /** * Created by Administrator on 2018/9/5. */ @RestController @RequestMapping("/core/user") public class UserController { @Autowired private UserService userService; /** * 登陸 * @param * @return */ @GetMapping("/login") public String login(String userName, String password) { System.out.println("登陸" + userName); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("admin-pc==" + userName, password); subject.login(token); Session session = subject.getSession(); Serializable sessionId = session.getId(); System.out.println("登陸成功 -> " + sessionId); return userName + "[" + sessionId + "]"; } @GetMapping("/logout") public String logout() { SecurityUtils.getSubject().logout(); return "退出登陸成功"; } /** * 獲取當前登陸用戶 * @return */ @GetMapping("/findUser") public String findUser() { Subject subject = SecurityUtils.getSubject(); PrincipalCollection collection = subject.getPrincipals(); if (null != collection && !collection.isEmpty()) { String userName = (String) collection.iterator().next(); System.out.println("獲取當前登陸用戶" + userName); return userService.findOneByUserName(userName).toString(); } return "{\n" + " \"codeEnum\": \"OVERTIME\",\n" + " \"code\": 0,\n" + " \"data\": null,\n" + " \"msg\": \"未登錄/登錄超時\",\n" + " \"success\": false\n" + "}"; } }
修改受權相關 UserShiroRealm.java:
package webapp.shiro; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.eis.SessionDAO; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.support.DefaultSubjectContext; import org.springframework.beans.factory.annotation.Autowired; import webapp.model.RolesPermissions; import webapp.model.User; import webapp.service.RolesPermissionsService; import webapp.service.UserService; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; /** * Created by Administrator on 2018/9/5. */ public class UserShiroRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private SessionDAO sessionDAO; @Autowired private RolesPermissionsService rolesPermissionsService; /** * 角色權限和對應權限添加 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String userName = (String) principalCollection.getPrimaryPrincipal(); if (userName == null || "".equals(userName)) { return null; }else{ userName = userName.split("==")[1]; SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); try{ List<RolesPermissions> rolesPermissions = rolesPermissionsService.selectByAuserName(userName); //當前用戶具備的權限 List<String> roles = rolesPermissions.stream().map(RolesPermissions::getRolesName).collect(Collectors.toList()); authorizationInfo.addRoles(roles); }catch(Exception e){ e.printStackTrace(); } return authorizationInfo; } } /** * 用戶認證 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //加這一步的目的是在Post請求的時候會先進認證,而後在到請求 if (authenticationToken.getPrincipal() == null) { return null; } String userName = authenticationToken.getPrincipal().toString(); //只容許同一帳戶單個登陸 Subject subject = SecurityUtils.getSubject(); Session nowSession = subject.getSession(); Collection<Session> sessions = sessionDAO.getActiveSessions(); if(sessions != null && sessions.size() > 0) { for (Session session : sessions) { if (!nowSession.getId().equals(session.getId()) && (session.getTimeout() == 0 || userName.equals(String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY))))) { sessionDAO.delete(session); } } } User user = userService.findOneByUserName(userName.contains("admin-pc==") ? userName.split("==")[1] : userName); if (user == null) { return null; } else { //這裏驗證authenticationToken和simpleAuthenticationInfo的信息 return new SimpleAuthenticationInfo(userName, user.getPassword(), getName()); } } }
自定義角色權限校驗器roleOrFilter(注:默認的roles爲and關係,因爲實際項目須要更多爲或者關係,故自定義此類) CustomRolesAuthorizationFilter.java:
package webapp.shiro; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authz.AuthorizationFilter; import webapp.model.RolesPermissions; import webapp.redis.RedisCache; import webapp.service.RolesPermissionsService; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class CustomRolesAuthorizationFilter extends AuthorizationFilter { //項目名-用於多項目共用redis緩存時區分項目 private static final String PROJECTNAME = "shiro5"; private RolesPermissionsService rolesPermissionsService; private RedisCache redisCache; public CustomRolesAuthorizationFilter(RolesPermissionsService rolesPermissionsService, RedisCache redisCache) { this.rolesPermissionsService = rolesPermissionsService; this.redisCache = redisCache; } @Override public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { Subject subject = getSubject(request, response); PrincipalCollection principals = subject.getPrincipals(); //未登陸狀況 if (null == principals) { return false; } String userName = principals.toString(); String userName0 = userName.split("==")[0]; String[] rolesArray = (String[]) mappedValue; if (rolesArray == null || rolesArray.length == 0) { return true; } //當前請求URI所須要的角色 List<String> rolesList = Arrays.asList(rolesArray); String userName1 = ""; //redis緩存中當前登陸用戶的角色(注:更改用戶角色時須要更新此緩存) Object cache = redisCache.getCache(PROJECTNAME + "==isAccessAllowed==" + userName); if ("admin-pc".equals(userName0)) { if (null != cache) { Set<String> cache1 = (Set<String>) cache; boolean disjoint = Collections.disjoint(cache1, rolesList); return !disjoint; } userName1 = userName.split("==")[1]; } List<RolesPermissions> rolesPermissions; if ("admin-pc".equals(userName0)) { rolesPermissions = rolesPermissionsService.selectByAuserName(userName1); } else { return true; } //當前用戶具備的權限 Set<String> roles = rolesPermissions.stream().map(RolesPermissions::getRolesName).collect(Collectors.toSet()); //往redis緩存中存儲當前登陸用戶的角色(注:更改用戶角色時須要更新此緩存) redisCache.putCacheWithExpireTime(PROJECTNAME + "==isAccessAllowed==" + userName, roles, 86400); //24小時過時 boolean disjoint = Collections.disjoint(roles, rolesList); return !disjoint; } }
使用自定義的角色權限校驗器roleOrFilter,ShiroPermissionFactory.java:
package webapp.shiro; import org.apache.shiro.config.Ini; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.config.IniFilterChainResolverFactory; import org.springframework.util.CollectionUtils; import webapp.model.RolesPermissions; import webapp.service.RolesPermissionsService; import java.util.*; public class ShiroPermissionFactory extends ShiroFilterFactoryBean { /**記錄配置中的過濾鏈*/ public static String definition = ""; private RolesPermissionsService rolesPermissionsService; public ShiroPermissionFactory(RolesPermissionsService rolesPermissionsService){ this.rolesPermissionsService = rolesPermissionsService; } /** * 初始化設置過濾鏈 */ @Override public void setFilterChainDefinitions(String definitions) { definition = definitions;//記錄配置的靜態過濾鏈 List<RolesPermissions> rolesPermissions = rolesPermissionsService.selectList(); Set<String> urls = new LinkedHashSet<>(); for (RolesPermissions rolesPermission : rolesPermissions) { urls.add(rolesPermission.getUrl()); } Map<String,String> otherChains = new HashMap<>(); for (String url : urls) { StringBuilder roleOrFilters = new StringBuilder(); int j = 0; for (int i = 0; i < rolesPermissions.size(); i++) { if (Objects.equals(url, rolesPermissions.get(i).getUrl())) { if (j == 0) { j = 1; roleOrFilters.append(rolesPermissions.get(i).getRolesName()); } else { roleOrFilters.append(",").append(rolesPermissions.get(i).getRolesName()); } } } String rolesStr = roleOrFilters.toString(); if (!"".equals(rolesStr)) { otherChains.put(url, "roleOrFilter[" + rolesStr + "]"); } } //加載配置默認的過濾鏈 Ini ini = new Ini(); ini.load(definitions); Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS); if (CollectionUtils.isEmpty(section)) { section = ini.getSection(Ini.DEFAULT_SECTION_NAME); } //加上數據庫中過濾鏈 section.putAll(otherChains); setFilterChainDefinitionMap(section); } }
shiro配置類ShiroConfig.java修改以下:
package webapp.conf; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import webapp.redis.RedisCache; import webapp.redis.RedisSessionDAO; import webapp.service.impl.RolesPermissionsServiceImpl; import webapp.shiro.CustomRolesAuthorizationFilter; import webapp.shiro.ShiroPermissionFactory; import webapp.shiro.UserShiroRealm; import javax.servlet.Filter; import java.util.HashMap; import java.util.Map; /** * shiro配置類 * Created by Administrator on 2018/9/5. */ @Configuration public class ShiroConfig { //將本身的驗證方式加入容器 @Bean public UserShiroRealm userShiroRealm() { return new UserShiroRealm(); } @Bean public SimpleCookie getSimpleCookie() { SimpleCookie simpleCookie = new SimpleCookie(); simpleCookie.setName("SHRIOSESSIONID"); return simpleCookie; } //保證明現了Shiro內部lifecycle函數的bean執行 @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } //配置shiro session 的一個管理器 @Bean(name = "sessionManager") public DefaultWebSessionManager getDefaultWebSessionManager(RedisSessionDAO redisSessionDAO) { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionDAO(redisSessionDAO); sessionManager.setGlobalSessionTimeout(-1000); //session有效期 默認值1800000 30分鐘 1800000毫秒 -1000表示永久 SimpleCookie simpleCookie = getSimpleCookie(); simpleCookie.setHttpOnly(true); //設置js不可讀取此Cookie simpleCookie.setMaxAge(3 * 365 * 24 * 60 * 60); //3年 cookie有前期 sessionManager.setSessionIdCookie(simpleCookie); return sessionManager; } //配置核心安全事務管理器 @Bean(name="securityManager") public SecurityManager securityManager(@Qualifier("userShiroRealm") UserShiroRealm userShiroRealm, @Qualifier("sessionManager") DefaultWebSessionManager sessionManager) { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(userShiroRealm); manager.setSessionManager(sessionManager); return manager; } //權限管理,配置主要是Realm的管理認證 @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(userShiroRealm()); return securityManager; } //Filter工廠,設置對應的過濾條件和跳轉條件 @Bean public ShiroPermissionFactory shiroPermissionFactory(RedisCache redisCache) { ShiroPermissionFactory shiroFilter = new ShiroPermissionFactory(new RolesPermissionsServiceImpl()); shiroFilter.setSecurityManager(securityManager()); //登陸認證不經過跳轉 shiroFilter.setLoginUrl("/loginUnAuth"); //權限認證不經過跳轉 shiroFilter.setUnauthorizedUrl("/authorUnAuth"); Map<String, Filter> filters = new HashMap<>(); filters.put("roleOrFilter", new CustomRolesAuthorizationFilter(new RolesPermissionsServiceImpl(), redisCache)); shiroFilter.setFilters(filters); shiroFilter.setFilterChainDefinitions("/###/@@@/** = authc"); return shiroFilter; } //加入註解的使用,不加入這個註解不生效 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } }
角色權限刷新器FilterChainDefinitions.java:
package webapp.shiro; import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager; import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver; import org.apache.shiro.web.servlet.AbstractShiroFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.Map; /** * 當修改用戶角色權限時候須要調用reloadFilterChains()來刷新角色權限 */ @Component public class FilterChainDefinitions { @Autowired private ShiroPermissionFactory permissFactory; public void reloadFilterChains() { synchronized (permissFactory) { //強制同步,控制線程安全 AbstractShiroFilter shiroFilter = null; try { shiroFilter = (AbstractShiroFilter) permissFactory.getObject(); PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) shiroFilter .getFilterChainResolver(); // 過濾管理器 DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager(); // 清除權限配置 manager.getFilterChains().clear(); permissFactory.getFilterChainDefinitionMap().clear(); // 從新設置權限 permissFactory.setFilterChainDefinitions(ShiroPermissionFactory.definition);//傳入配置中的filterchains Map<String, String> chains = permissFactory.getFilterChainDefinitionMap(); //從新生成過濾鏈 if (!CollectionUtils.isEmpty(chains)) { chains.forEach((url, definitionChains) -> { manager.createChain(url, definitionChains.trim().replace(" ", "")); }); } } catch (Exception e) { e.printStackTrace(); } } } }
角色權限刷新器FilterChainDefinitions.java的使用示例TestController.java:
package webapp.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import webapp.shiro.FilterChainDefinitions; @RestController @RequestMapping("/core/test") public class TestController { @Autowired private FilterChainDefinitions filterChainDefinitions; @GetMapping("/test") public void test() { //設置用戶權限 //... //刷新權限 filterChainDefinitions.reloadFilterChains(); } }
而後啓動項目,RolesPermissionsServiceImpl.java類中的獲取數據庫(模擬)中全部角色權限和獲取用戶角色權限可自行修改測試。實際應用於項目也只須要修改這個類爲查詢實際數據庫數據便可。