(十二)springboot中shiro的使用
阿新 • • 發佈:2018-12-18
一、引入maven配置
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
二、建表
使用者表、角色表、許可權表、使用者角色表、角色許可權表。
使用者表:
角色表:
許可權表:news:* 表示有新聞的所有許可權(包括增刪改查),而news:add,只有新聞的新增許可權。
使用者角色表:使用者擁有哪些角色。
角色許可權表:角色擁有哪些許可權。
三、自定義Realm
自定義realm主要用於使用者的許可權驗證以及登入驗證。
自定義realm繼承AuthorizingRealm類,並重寫doGetAuthorizationInfo和doGetAuthenticationInfo方法,doGetAuthorizationInfo方法主要校驗使用者的許可權,即該使用者擁有什麼角色以及許可權。doGetAuthenticationInfo用於校驗登入驗證,即使用者的使用者名稱或者密碼是否正確。
/** * 類名 : 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配置類
/** * 類名 :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 不生效的原因:1、map的型別必須是LinkedHashMap 2、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層捕獲並處理異常。
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("系統錯誤,請檢視日誌"); }