<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
用戶表、角色表、權限表、用戶角色表、角色權限表。html
用戶表:java
角色表:spring
權限表:news:* 表示有新聞的全部權限(包括增刪改查),而news:add,只有新聞的新增權限。apache
用戶角色表:用戶擁有哪些角色。安全
角色權限表:角色擁有哪些權限。app
自定義realm主要用於用戶的權限驗證以及登陸驗證。maven
自定義realm繼承AuthorizingRealm類,並重寫doGetAuthorizationInfo和doGetAuthenticationInfo方法,doGetAuthorizationInfo方法主要校驗用戶的權限,即該用戶擁有什麼角色以及權限。doGetAuthenticationInfo用於校驗登陸驗證,即用戶的用戶名或者密碼是否正確。ide
/** * 類名 : shiro的Realm * 用法 : * 建立人 : shyroke * 時間:2018/12/12 17:29 */ public class MyRealm extends AuthorizingRealm { @Autowired private UserMapper userMapper; @Autowired private PermissionMapper permissionMapper; /** * 受權,即該用戶擁有什麼角色以及權限 * 步驟:根據用戶名獲取角色以及權限,而後設置到驗證信息類並返回。 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); String userName = principalCollection.getPrimaryPrincipal().toString(); // 獲取該用戶的角色 List<Role> roles = userMapper.getRolesByUserName(userName); Set<String> roleSets = new HashSet<>(); // 獲取該用戶的權限集 List<Permission> permissions = new ArrayList<>(); Set<String> permissoinSet = new HashSet<>(); //將List轉爲Set if(roles !=null && roles.size()>0){ for(Role role:roles){ roleSets.add(role.getName()); permissions.addAll(permissionMapper.getPermissionByRoleId(role.getId())); } } //將List轉爲Set if(permissions!=null & permissoinSet.size()>0){ for(Permission p :permissions){ permissoinSet.add(p.getName()); } } info.addRoles(roleSets); info.addStringPermissions(permissoinSet); return info; } /** * 認證,即用戶帳號密碼是否正確 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 獲取用戶名和密碼 String userName = (String)token.getPrincipal(); String passWord = new String((char[]) token.getCredentials()); // 根據用戶名查找該用戶 User user = userMapper.getUserByName(userName); if(user == null){ throw new UnknownAccountException("用戶名不存在"); } if(!user.getPassword().equals(passWord)){ throw new IncorrectCredentialsException("密碼錯誤"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getName(),user.getPassword(),getName()); return info; } }
/** * 類名 :shiro的核心配置類 * 用法 : * 建立人 : shyroke * 時間:2018/12/14 15:20 */ @Configuration public class ShiroConfigration { // 設置自定義Realm @Bean public MyRealm myRealm(){ return new MyRealm(); } // 權限管理,配置主要是Realm的管理認證 @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(myRealm()); return manager; } /** * 設置過濾條件和跳轉條件 * anon 不生效的緣由:一、map的類型必須是LinkedHashMap 二、anon必須定義在authc以前 * * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); // 設置登陸跳轉 factoryBean.setLoginUrl("/admin"); factoryBean.setSuccessUrl("/admin/index"); //必須爲LinkedHashMap 不然anon不生效 Map<String,String> map = new LinkedHashMap<>(); //退出 map.put("/admin/logout","logout"); //登陸頁面和登陸驗證不要攔截 map.put("/admin/login.html","anon"); map.put("/admin/tologin","anon"); //設置須要過濾的連接 map.put("/admin/**","authc"); factoryBean.setFilterChainDefinitionMap(map); return factoryBean; } /** * 開啓Shiro的註解(如@RequiresRoles,@RequiresPermissions),需藉助SpringAOP掃描使用Shiro註解的類,並在必要時進行安全邏輯驗證 * 配置如下兩個bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)便可實現此功能 * @return */ @Bean public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } /** * 開啓aop註解支持 * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
代碼以下,當調用Subject.login方法後,會調用自定義Realm的doGetAuthenticationInfo方法校驗用戶名密碼是否正確,若是不正確則拋出對應異常,controller層捕獲並處理異常。ui
AuthenticationToken usernamePasswordToken = new UsernamePasswordToken(user.getName(),user.getPassword()); Subject subject = SecurityUtils.getSubject(); try { subject.login(usernamePasswordToken); }catch (UnknownAccountException ex) { logger.info("用戶名錯誤!"); return R.error("用戶名錯誤!"); } catch (IncorrectCredentialsException ex) { logger.info("密碼錯誤!"); return R.error("密碼錯誤!"); } catch (AuthenticationException ex) { logger.info(ex.getMessage()); return R.error("系統錯誤,請查看日誌"); } catch (Exception ex) { logger.info(ex.getMessage()); return R.error("系統錯誤,請查看日誌"); }