使用shiro安全管理
之前介紹了springboot使用security進行權(quán)限管理,這篇文件介紹一下springboot使用shiro進行安全管理。
簡述本文的場景,本文使用springboot1.5.9+mysql+jpa+thymeleaf+shiro制作一個簡單的驗證,其中有2個角色,分別是admin和user,admin可以使用select和delete功能,user只能使用select功能。
新建項目,加入shiro依賴,pom文件如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.dalaoyang</groupId><artifactId>springboot_shiro</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>springboot_shiro</name><description>springboot_shiro</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.9.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>net.sourceforge.nekohtml</groupId><artifactId>nekohtml</artifactId><version>1.9.15</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>配置文件如下:
##端口號 server.port=8888##數(shù)據(jù)庫配置 ##數(shù)據(jù)庫地址 spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=utf8&useSSL=false ##數(shù)據(jù)庫用戶名 spring.datasource.username=root ##數(shù)據(jù)庫密碼 spring.datasource.password=root ##數(shù)據(jù)庫驅(qū)動 spring.datasource.driver-class-name=com.mysql.jdbc.Driver##validate 加載hibernate時,驗證創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu) ##create 每次加載hibernate,重新創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu),這就是導(dǎo)致數(shù)據(jù)庫表數(shù)據(jù)丟失的原因。 ##create-drop 加載hibernate時創(chuàng)建,退出是刪除表結(jié)構(gòu) ##update 加載hibernate自動更新數(shù)據(jù)庫結(jié)構(gòu) ##validate 啟動時驗證表的結(jié)構(gòu),不會創(chuàng)建表 ##none 啟動時不做任何操作 spring.jpa.hibernate.ddl-auto=update##控制臺打印sql spring.jpa.show-sql=true# 建議在開發(fā)時關(guān)閉緩存,不然沒法看到實時頁面 spring.thymeleaf.cache=false ##去除thymeleaf的html嚴(yán)格校驗 spring.thymeleaf.mode=LEGACYHTML5創(chuàng)建了三個實體類,分別是
SysUser(用戶表)
SysRole(角色表)
package com.dalaoyang.entity;import org.hibernate.validator.constraints.NotEmpty;import javax.persistence.*; import java.io.Serializable; import java.util.List;/*** @author dalaoyang* @Description* @project springboot_learn* @package com.dalaoyang.entity* @email yangyang@dalaoyang.cn* @date 2018/5/2*/ @Entity public class SysRole implements Serializable {@Id@GeneratedValueprivate Integer roleId;private String roleName;//多對多關(guān)系@ManyToMany(fetch= FetchType.EAGER)@JoinTable(name="SysRoleMenu",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="menuId")})private List<SysMenu> menuList;//多對多關(guān)系@ManyToMany@JoinTable(name="SysUserRole",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="userId")})private List<SysUser> userList;// 一個角色對應(yīng)多個用戶public Integer getRoleId() {return roleId;}public void setRoleId(Integer roleId) {this.roleId = roleId;}public String getRoleName() {return roleName;}public void setRoleName(String roleName) {this.roleName = roleName;}public List<SysMenu> getMenuList() {return menuList;}public void setMenuList(List<SysMenu> menuList) {this.menuList = menuList;}public List<SysUser> getUserList() {return userList;}public void setUserList(List<SysUser> userList) {this.userList = userList;} }SysMenu(菜單表)
package com.dalaoyang.entity;import javax.persistence.*; import java.io.Serializable; import java.util.List;/*** @author dalaoyang* @Description* @project springboot_learn* @package com.dalaoyang.entity* @email yangyang@dalaoyang.cn* @date 2018/5/2*/ @Entity public class SysMenu implements Serializable {@Id@GeneratedValueprivate Integer menuId;private String menuName;@ManyToMany@JoinTable(name="SysRoleMenu",joinColumns={@JoinColumn(name="menuId")},inverseJoinColumns={@JoinColumn(name="roleId")})private List<SysRole> roleList;public Integer getMenuId() {return menuId;}public void setMenuId(Integer menuId) {this.menuId = menuId;}public String getMenuName() {return menuName;}public void setMenuName(String menuName) {this.menuName = menuName;}public List<SysRole> getRoleList() {return roleList;}public void setRoleList(List<SysRole> roleList) {this.roleList = roleList;} }創(chuàng)建一個UserRepository用于查詢用戶信息:
package com.dalaoyang.repository;import com.dalaoyang.entity.SysUser; import org.springframework.data.repository.CrudRepository;/*** @author dalaoyang* @Description* @project springboot_learn* @package com.dalaoyang.repository* @email yangyang@dalaoyang.cn* @date 2018/5/2*/ public interface UserRepository extends CrudRepository<SysUser,Long> {SysUser findByUserName(String username); }創(chuàng)建幾個前臺頁面進行測試,分別是:
login.html:
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> index <br/> <form th:action="@{/logout}" method="post"><p><input type="submit" value="注銷"/></p> </form> </body> </html>delete.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> delete </body> </html>select.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> select </body> </html>403.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> 403 </body> </html>創(chuàng)建一個ShiroConfig,代碼如下:
package com.dalaoyang.config;import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties;/*** @author dalaoyang* @Description* @project springboot_learn* @package com.dalaoyang.config* @email yangyang@dalaoyang.cn* @date 2018/5/2*/ @Configuration public class ShiroConfig {private final Logger logger = LoggerFactory.getLogger(this.getClass());@Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {logger.info("啟動shiroFilter--時間是:" + new Date());ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);//shiro攔截器Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();//<!-- authc:所有url都必須認(rèn)證通過才可以訪問; anon:所有url都都可以匿名訪問-->//<!-- 過濾鏈定義,從上向下順序執(zhí)行,一般將/**放在最為下邊 -->// 配置不被攔截的資源及鏈接filterChainDefinitionMap.put("/static/**", "anon");// 退出過濾器filterChainDefinitionMap.put("/logout", "logout");//配置需要認(rèn)證權(quán)限的filterChainDefinitionMap.put("/**", "authc");// 如果不設(shè)置默認(rèn)會自動尋找Web工程根目錄下的"/login"頁面,即本文使用的login.htmlshiroFilterFactoryBean.setLoginUrl("/login");// 登錄成功后要跳轉(zhuǎn)的鏈接shiroFilterFactoryBean.setSuccessUrl("/index");//未授權(quán)界面shiroFilterFactoryBean.setUnauthorizedUrl("/403");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}//自定義身份認(rèn)證Realm(包含用戶名密碼校驗,權(quán)限校驗等)@Beanpublic MyShiroRealm myShiroRealm(){MyShiroRealm myShiroRealm = new MyShiroRealm();return myShiroRealm;}@Beanpublic SecurityManager securityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myShiroRealm());return securityManager;}//開啟shiro aop注解支持,不開啟的話權(quán)限驗證就會失效@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}//配置異常處理,不配置的話沒有權(quán)限后臺報錯,前臺不會跳轉(zhuǎn)到403頁面@Bean(name="simpleMappingExceptionResolver")public SimpleMappingExceptionResolvercreateSimpleMappingExceptionResolver() {SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();Properties mappings = new Properties();mappings.setProperty("DatabaseException", "databaseError");//數(shù)據(jù)庫異常處理mappings.setProperty("UnauthorizedException","403");simpleMappingExceptionResolver.setExceptionMappings(mappings); // None by defaultsimpleMappingExceptionResolver.setDefaultErrorView("error"); // No defaultsimpleMappingExceptionResolver.setExceptionAttribute("ex"); // Default is "exception"return simpleMappingExceptionResolver;} }在配置一個MyShiroRealm用于登錄認(rèn)證和授權(quán)認(rèn)證,代碼如下:
package com.dalaoyang.config;import com.dalaoyang.entity.SysMenu; import com.dalaoyang.entity.SysRole; import com.dalaoyang.entity.SysUser; import com.dalaoyang.repository.UserRepository; 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.subject.PrincipalCollection;import javax.annotation.Resource;/*** @author dalaoyang* @Description* @project springboot_learn* @package com.dalaoyang.config* @email yangyang@dalaoyang.cn* @date 2018/5/2*/ public class MyShiroRealm extends AuthorizingRealm {@Resourceprivate UserRepository userRepository;//授權(quán)@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();SysUser userInfo = (SysUser)principals.getPrimaryPrincipal();for(SysRole role:userInfo.getRoleList()){authorizationInfo.addRole(role.getRoleName());for(SysMenu menu:role.getMenuList()){authorizationInfo.addStringPermission(menu.getMenuName());}}return authorizationInfo;}//認(rèn)證@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException {//獲得當(dāng)前用戶的用戶名String username = (String)token.getPrincipal();System.out.println(token.getCredentials());//根據(jù)用戶名找到對象//實際項目中,這里可以根據(jù)實際情況做緩存,如果不做,Shiro自己也是有時間間隔機制,2分鐘內(nèi)不會重復(fù)執(zhí)行該方法SysUser userInfo = userRepository.findByUserName(username);if(userInfo == null){return null;}//這里會去校驗密碼是否正確SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo, //用戶名userInfo.getPassWord(),//密碼getName());return authenticationInfo;} }最后新建一個controller,其中本文使用了2種驗證權(quán)限的方法,select方法使用@RequiresPermissions("select")來驗證用戶是否具有select權(quán)限,delete方法使用@RequiresRoles("admin")來驗證用戶是否是admin,代碼如下:
package com.dalaoyang.controller;import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest; import java.util.Map;/*** @author dalaoyang* @Description* @project springboot_learn* @package com.dalaoyang.controller* @email yangyang@dalaoyang.cn* @date 2018/5/2*/ @Controller public class TestController {@GetMapping({"/","/index"})public String index(){return"index";}@GetMapping("/403")public String unauthorizedRole(){return "403";}@GetMapping("/delete")//@RequiresPermissions("delete")@RequiresRoles("admin")public String delete(){return "delete";}@GetMapping("/select")@RequiresPermissions("select")public String select(){return "select";}@RequestMapping("/login")public String login(HttpServletRequest request, Map<String, Object> map) throws Exception{System.out.println("HomeController.login()");// 登錄失敗從request中獲取shiro處理的異常信息。// shiroLoginFailure:就是shiro異常類的全類名.String exception = (String) request.getAttribute("shiroLoginFailure");String msg = "";//根據(jù)異常判斷錯誤類型if (exception != null) {if (UnknownAccountException.class.getName().equals(exception)) {msg = "賬號不存在";} else if (IncorrectCredentialsException.class.getName().equals(exception)) {msg = "密碼不正確";} else {msg = "else >> "+exception;}}map.put("msg", msg);// 此方法不處理登錄成功,由shiro進行處理return "/login";}@GetMapping("/logout")public String logout(){return "/login";} }為了方便測試,本人插入了幾條初始數(shù)據(jù),sql如下:
INSERT INTO `shiro`.`sys_menu`(`menu_id`, `menu_name`) VALUES (1, 'add'); INSERT INTO `shiro`.`sys_menu`(`menu_id`, `menu_name`) VALUES (2, 'delete'); INSERT INTO `shiro`.`sys_menu`(`menu_id`, `menu_name`) VALUES (3, 'update'); INSERT INTO `shiro`.`sys_menu`(`menu_id`, `menu_name`) VALUES (4, 'select'); INSERT INTO `shiro`.`sys_role`(`role_id`, `role_name`) VALUES (1, 'admin'); INSERT INTO `shiro`.`sys_role`(`role_id`, `role_name`) VALUES (2, 'user'); INSERT INTO `shiro`.`sys_role_menu`(`role_id`, `menu_id`) VALUES (1, 1); INSERT INTO `shiro`.`sys_role_menu`(`role_id`, `menu_id`) VALUES (1, 2); INSERT INTO `shiro`.`sys_role_menu`(`role_id`, `menu_id`) VALUES (1, 3); INSERT INTO `shiro`.`sys_role_menu`(`role_id`, `menu_id`) VALUES (1, 4); INSERT INTO `shiro`.`sys_role_menu`(`role_id`, `menu_id`) VALUES (2, 4); INSERT INTO `shiro`.`sys_user`(`user_id`, `pass_word`, `user_name`) VALUES (1, '123', 'dalaoyang'); INSERT INTO `shiro`.`sys_user`(`user_id`, `pass_word`, `user_name`) VALUES (2, '123', 'xiaoli'); INSERT INTO `shiro`.`sys_user_role`(`role_id`, `user_id`) VALUES (1, 1); INSERT INTO `shiro`.`sys_user_role`(`role_id`, `user_id`) VALUES (2, 2);啟動項目,我在這里就不一一截圖了,口述一下,訪問http://localhost:8888/select由于沒有登錄的原因,會自動跳轉(zhuǎn)到http://localhost:8888/login,輸入錯誤的用戶名和密碼會出現(xiàn)對應(yīng)的提示。輸入角色user的用戶名xiaoli,密碼123。訪問http://localhost:8888/select頁面會正常跳轉(zhuǎn),訪問http://localhost:8888/delete會攔截到403頁面。
如果使用角色為admin的用戶dalaoyang密碼123登錄,以上請求全可以正常訪問。
源碼下載 :大老楊碼云
個人網(wǎng)站:https://dalaoyang.cn
轉(zhuǎn)載于:https://www.cnblogs.com/dalaoyang/p/8981285.html
總結(jié)
以上是生活随笔為你收集整理的使用shiro安全管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cohen_sutherland的直线裁
- 下一篇: 洛谷 P1897电梯里的爱情 题解