shiro是一款權限控制的後臺框架,能夠控制用戶-角色-權限的分配,例如資源和路徑等等。html
主要內容:java
ShiroFilterFactoryBean工廠類mysql
SecurityManager安全管理器web
MyRealm extends AuthorizingRealm自定義Realm,實現doGetAuthenticationInfo(認證)和doGetAuthorizationInfo(受權)方法。spring
還有其餘一些rememberMe、Session和Cache功能。sql
下面使用SpringBoot整合Shiro的例子簡單說明apache
表:數據中用戶密碼要存儲加密後的密文安全
pom.xml文件cookie
<?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>online.myson</groupId> <artifactId>shiro</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>Shiro</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.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-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <!-- Spring Data JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.0.1.RELEASE</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </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-devtools</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
shiro配置類app
package online.myson.config; import java.util.LinkedHashMap; import java.util.Map; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.crypto.hash.SimpleHash; 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.util.ByteSource; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import online.myson.shiro.MyRealm; @Configuration public class ShiroConfig { /** * 註冊ShiroFilterFactoryBean,實際上是註冊一個filter */ @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 設置securityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 設置攔截器 Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>(); // 過濾鏈定義,從上向下執行,通常將/**放最下面 // authc:全部的url都必須認證經過才能夠訪問;anon:全部url均可以匿名訪問 filterChainDefinitionMap.put("/statics/**", "anon"); filterChainDefinitionMap.put("/login.html", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/user/**", "user"); filterChainDefinitionMap.put("/*", "authc"); // 不設置自動尋找根目錄下的/login.jsp頁面 shiroFilterFactoryBean.setLoginUrl("/index"); // 登陸成功後要跳轉的鏈接 shiroFilterFactoryBean.setSuccessUrl("/index"); // 未受權頁面 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 註冊securityManager */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm()); securityManager.setRememberMeManager(cookieRememberMeManager()); return securityManager; } /** * 註冊自定義Realm */ @Bean public MyRealm myRealm() { MyRealm myRealm = new MyRealm(); myRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myRealm; } /** * 註冊HashedCredentialsMatcher,加解密 */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5"); hashedCredentialsMatcher.setHashIterations(2); return hashedCredentialsMatcher; } /** * 註冊CookieRememberMeManager */ @Bean public CookieRememberMeManager cookieRememberMeManager() { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(simpleCookie()); return cookieRememberMeManager; } /** * 註冊SimpleCookie */ @Bean public SimpleCookie simpleCookie() { SimpleCookie simpleCookie = new SimpleCookie(); simpleCookie.setPath("/"); simpleCookie.setHttpOnly(true); simpleCookie.setMaxAge(604800);// 時間 return simpleCookie; } /** * 開啓shiro aop支持 * 用於受權 */ @Bean public AuthorizationAttributeSourceAdvisor AuthorizationAttributeSourceAdvisor( SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } public static void main(String[] args) { Object obj = new SimpleHash("md5","123456",ByteSource.Util.bytes("user"),2); System.out.println(obj); } }
一、認證
// 認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 獲取用戶輸入的帳號 String username = (String)token.getPrincipal(); System.out.println(token.getCredentials()); // 查詢是否存在帳號 SysUser user = sysUserService.findByUsername(username); System.out.println(user); if(user == null) { return null; } // 驗證密碼 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()),// salt "MyRealm"// realmName ); return authenticationInfo; }
Controller類:
@PostMapping("/login") public ModelAndView login(SysUser user) { ModelAndView mv = new ModelAndView("/login"); String msg = "成功"; Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword()); try { subject.login(token); }catch(Exception e) { if(e instanceof UnknownAccountException) { msg = "帳戶不存在"; }else if(e instanceof IncorrectCredentialsException) { msg = "密碼不正確"; }else { msg = e.getMessage(); } } mv.addObject("msg", msg); return mv; }
二、受權
三種方式:
a、經過Subject.isPermitted("userInfo:view")和subject.hasRole("admin")的一些方法,if else來控制
b、經過註解AOP控制
c、jsp頁面使用標籤<shiro>控制
// 受權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); SysUser user = (SysUser) principals.getPrimaryPrincipal(); // 獲取用戶的角色和對應的權限,這裏一次性查角色和權限了,也能夠寫sql查 for(SysRole role : user.getRoleList()) { authorizationInfo.addRole(role.getRole()); for(SysPermission permission : role.getPermissions()) { authorizationInfo.addStringPermission(permission.getPermission()); } } return authorizationInfo; }
Controller:
/** * 用戶添加 */ @GetMapping("/user/add") @RequiresPermissions("userInfo:view") public String userAdd() { Subject subject = SecurityUtils.getSubject(); // if(subject.isPermitted("userInfo:view")) { // return "userAdd"; // }else { // return "no authc"; // } return "userAdd";