springboot入门_shiro
shiro是apache的一款開元安全框架,可以進行用戶身份的認證,授權,session管理,加密等。相對于springsecurity來說比較簡單,容易理解和使用。下來我們實現(xiàn)在springboot項目中使用shiro框架。
我們使用RBAC(基于角色的訪問控制)來設計角色權限關系,有3個實體:用戶,角色,權限,在RBAC中用戶擁有某一種角色,角色擁有一個或者多個資源,所以表結構設計如下:
1 用戶信息 2 DROP TABLE IF EXISTS `user_info`; 3 CREATE TABLE `user_info` ( 4 `id` int(11) NOT NULL AUTO_INCREMENT, 5 `username` varchar(50) DEFAULT NULL, 6 `password` varchar(32) DEFAULT NULL, 7 `email` varchar(50) DEFAULT NULL, 8 `usertype` char(1) DEFAULT NULL, 9 PRIMARY KEY (`id`) 10 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 11 12 角色信息 13 DROP TABLE IF EXISTS `role_info`; 14 CREATE TABLE `role_info` ( 15 `id` int(11) NOT NULL AUTO_INCREMENT, 16 `rolename` varchar(50) DEFAULT NULL, 17 `roledesc` varchar(100) DEFAULT NULL, 18 PRIMARY KEY (`id`) 19 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 20 21 用戶角色關系 22 DROP TABLE IF EXISTS `user_role`; 23 CREATE TABLE `user_role` ( 24 `id` int(11) NOT NULL AUTO_INCREMENT, 25 `userid` int(11) NOT NULL, 26 `roleid` int(11) NOT NULL, 27 PRIMARY KEY (`id`) 28 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 29 30 資源(權限)信息 31 DROP TABLE IF EXISTS `resource_info`; 32 CREATE TABLE `resource_info` ( 33 `id` int(11) NOT NULL AUTO_INCREMENT, 34 `resourceName` varchar(50) NOT NULL, 35 `resourceUrl` varchar(50) NOT NULL, 36 `resourceType` char(1) NOT NULL COMMENT '1-菜單;2-按鈕', 37 `resourcePid` int(11) DEFAULT NULL COMMENT '上級,按鈕上級為菜單', 38 PRIMARY KEY (`id`) 39 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 40 41 角色資源關系 42 DROP TABLE IF EXISTS `role_resource`; 43 CREATE TABLE `role_resource` ( 44 `id` int(11) NOT NULL AUTO_INCREMENT, 45 `roleId` int(11) NOT NULL, 46 `resourceId` int(11) NOT NULL, 47 PRIMARY KEY (`id`) 48 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;?
下來實現(xiàn)springboot項目與shiro的整合,創(chuàng)建springboot項目并使用mybatis做數(shù)據(jù)庫訪問,不清楚springboot整合mybatis的朋友可以看我之前的筆記,我們假設已經有了項目,這里只說整合shiro。
1 在pom文件中添加對shiro的依賴
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version> </dependency>2 創(chuàng)建shiro配置信息類,完成配置
1 @Configuration 2 public class ShiroConfig { 3 4 /** 5 * shiro 過濾器 6 * @param securityManager 7 * @return 8 */ 9 @Bean 10 public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){ 11 ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); 12 shiroFilter.setSecurityManager(securityManager); 13 shiroFilter.setLoginUrl("/login");//登錄失敗后默認跳轉到登錄地址去 14 shiroFilter.setSuccessUrl("/main");//登錄成功后跳轉地址 15 16 /** 17 * 過濾器鏈, 從前往后 順序判斷 18 */ 19 Map<String, String> filterChain = new LinkedHashMap<String, String>(); 20 //記住我或者認證登錄通過可以訪問 21 filterChain.put("/main", "user"); 22 23 filterChain.put("/js/**", "anon");//設置靜態(tài)資源可以匿名訪問 24 filterChain.put("/logout", "logout");//logout shiro已經做了實現(xiàn) 25 filterChain.put("/login", "anon");//可匿名訪問 26 27 filterChain.put("/**", "authc");// 除了上邊配置的路徑及資源外,其他訪問都需要認證,此項必須放在最后 28 shiroFilter.setFilterChainDefinitionMap(filterChain); 29 30 shiroFilter.setUnauthorizedUrl("/unAuthPage");//沒有授權的頁面 31 return shiroFilter; 32 } 33 34 /** 35 * 安全管理器 36 * @return 37 */ 38 @Bean 39 public DefaultWebSecurityManager securityManager(){ 40 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 41 securityManager.setRealm(getShiroRealm());//自定義realm 42 securityManager.setRememberMeManager(rememberMeManager());//記住我管理器 43 return securityManager; 44 } 45 46 /** 47 * 自定義的realm 48 * @return 49 */ 50 @Bean 51 public ShiroRealm getShiroRealm(){ 52 ShiroRealm shiroRealm = new ShiroRealm(); 53 shiroRealm.setCredentialsMatcher(credentialsMatcher());//憑證匹配器 54 return shiroRealm; 55 } 56 57 /** 58 * 憑證匹配器 (密碼的校驗交給了shiro的SimpleAuthenticationInfo處理) 59 * @return 60 */ 61 @Bean 62 public HashedCredentialsMatcher credentialsMatcher(){ 63 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); 64 credentialsMatcher.setHashAlgorithmName("MD5");//散列算法名稱,如:md5 65 credentialsMatcher.setHashIterations(2);//散列算法的次數(shù) 66 return credentialsMatcher; 67 } 68 69 /** 70 * 記住我管理器 71 * @return 72 */ 73 @Bean 74 public CookieRememberMeManager rememberMeManager(){ 75 CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); 76 rememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag==")); 77 rememberMeManager.setCookie(cookie()); 78 return rememberMeManager; 79 } 80 81 /** 82 * 記住我 cookie 83 * @return 84 */ 85 @Bean 86 public SimpleCookie cookie(){ 87 SimpleCookie rememberMeCookie = new SimpleCookie("rememberMe"); 88 rememberMeCookie.setHttpOnly(true); 89 rememberMeCookie.setMaxAge(60);//記住我生效時間,單位 秒 90 return rememberMeCookie; 91 } 92 93 /** 94 * shiro生命周期 95 * @return 96 */ 97 @Bean 98 public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { 99 return new LifecycleBeanPostProcessor(); 100 } 101 102 }2 自定義的realm,實現(xiàn)認證和授權
1 public class ShiroRealm extends AuthorizingRealm { 2 3 @Autowired 4 private UserInfoService userInfoService; 5 @Autowired 6 private UserRoleService userRoleService; 7 @Autowired 8 private ResourceInfoService resourceInfoService; 9 10 /** 11 * 授權 12 */ 13 @Override 14 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 15 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 16 Set<String> permissionsSet = new HashSet<String>();//用來存放用戶所擁有的權限資源 17 //當前用戶 18 UserInfoEntity userInfo = (UserInfoEntity) principals.getPrimaryPrincipal();//如果認證方法中保存的是用戶賬號,此處獲取的是用戶的賬號,如果存的是用戶對象,則此處獲取的是用戶對象 19 //當前用戶角色 20 UserRole userRole = userRoleService.selectByUserId(userInfo.getId()); 21 if(userRole != null){ 22 List<ResourceInfo> resourceList = resourceInfoService.selectByRoleId(userRole.getRoleId()); 23 if(resourceList != null && resourceList.size()>0){ 24 for(int i=0; i<resourceList.size(); i++){ 25 ResourceInfo ri = resourceList.get(i); 26 permissionsSet.add(ri.getResourceUrl()); 27 } 28 } 29 } 30 info.setStringPermissions(permissionsSet); 31 return info; 32 } 33 34 /** 35 * 認證 36 */ 37 @Override 38 protected AuthenticationInfo doGetAuthenticationInfo( 39 AuthenticationToken authToken) throws AuthenticationException { 40 //將AuthenticationToken轉換為UsernamePasswordToken 41 UsernamePasswordToken token = (UsernamePasswordToken) authToken; 42 //獲取用戶名 43 String username = token.getUsername(); 44 //判斷當前用戶名在數(shù)據(jù)庫中是否存在 45 UserInfoEntity userInfo = userInfoService.selectByEmail(username); 46 if(userInfo == null){ 47 throw new UnknownAccountException("賬戶不存在"); 48 } 49 //從數(shù)據(jù)庫對象獲取的密碼 50 String password = userInfo.getPassword(); 51 52 //認證的實體信息,可以是用戶實體,也可以是用戶賬號 53 Object principal = userInfo; 54 //認證的密碼 55 Object credentials = password; 56 //加密的顏 57 ByteSource credentialsSalt = ByteSource.Util.bytes(username);//使用登錄的賬號(唯一性)作為顏 58 59 //通過SimpleAuthenticationInfo 認證 60 SimpleAuthenticationInfo authInfo = null; 61 authInfo = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, this.realmName()); 62 return authInfo; 63 } 64 65 public String realmName(){ 66 return "shiroRealm"; 67 } 68 }3 登錄實現(xiàn)
1 @Controller 2 public class LoginController { 3 4 private static final Logger logger = LogManager.getLogger(LoginController.class); 5 6 @RequestMapping("/login") 7 public String login(){ 8 logger.info("跳轉至登錄頁。。。"); 9 return "login"; 10 } 11 12 @RequestMapping(value="/login",method=RequestMethod.POST) 13 @ResponseBody 14 public String doLogin(@RequestParam("username") String username, @RequestParam("password") String password, 15 @RequestParam(value = "rememberMeParam", required = false) String rememberMeParam, Map<String, Object> map){ 16 UsernamePasswordToken token = new UsernamePasswordToken(username, password); 17 18 //記住我 19 if("Y".equals(rememberMeParam)){ 20 token.setRememberMe(true); 21 } 22 23 Subject subject = SecurityUtils.getSubject(); 24 try { 25 subject.login(token); 26 } catch (UnknownAccountException e) { 27 //e.printStackTrace(); 28 logger.info("賬號不存在"); 29 } catch (IncorrectCredentialsException e) { 30 //e.printStackTrace(); 31 logger.info("密碼不正確"); 32 } 33 34 if(subject.isAuthenticated()){ 35 map.put("result", "success"); 36 logger.info("登錄成功。。。"); 37 }else{ 38 map.put("result", "faile"); 39 map.put("msg", "賬號或密碼錯誤"); 40 } 41 return JSON.toJSONString(map); 42 } 43 44 @RequestMapping("/main") 45 public String main(Model model){ 46 Subject subject = SecurityUtils.getSubject(); 47 UserInfoEntity userInfo = (UserInfoEntity) subject.getPrincipal(); 48 model.addAttribute("currentUser", userInfo); 49 return "main"; 50 } 51 52 @RequestMapping("/unAuthPage") 53 public String unAuthPage(){ 54 return "unAuth"; 55 } 56 57 @RequestMapping("/logout") 58 public String logout(){ 59 Subject subject = SecurityUtils.getSubject(); 60 subject.logout(); 61 return "redirect:/login"; 62 } 63 64 }4 退出 退出功能shiro已經為我做了實現(xiàn),我們只需要調用Subject的logout()方法即可實現(xiàn)用戶退出。
5 測試?
在沒有登錄的情況下,如果我們訪問main方法,會被shiroFilter攔截,并將請求轉向loginUrl方法引導用戶登錄;登錄成功后會跳轉至successUrl;
在頁面上我們使用shiro的標簽來做權限過濾,需要在頁面引入shiro標簽庫:<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>,可以在菜單或者按鈕上加上權限過濾標簽如:
<shiro:hasPermission name="your perMission"><a href=".....">用戶管理</a> </shiro:hasPermission>?
在加載頁面時,遇到shiro標簽時會去后臺查詢用戶權限信息,如果用戶擁有當前標簽中name屬性指定的權限信息時會加載并顯示當前shiro標簽包含的頁面元素信息,否則不會顯示
?
轉載于:https://www.cnblogs.com/Allen-Wl/p/9304425.html
總結
以上是生活随笔為你收集整理的springboot入门_shiro的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊架构设计做些什么来谈如何成为架构师
- 下一篇: 数据结构:单调栈