安全無處不在,趁着放假讀了一下 Shiro 文檔,並記錄一下 Shiro 整合 Spring Boot 在數據庫中根據角色控制訪問權限html
Apache Shiro是一個功能強大、靈活的,開源的安全框架。它能夠乾淨利落地處理身份驗證、受權、企業會話管理和加密。java
上圖是 Shiro 的基本架構mysql
有時被稱爲「登陸」,用來證實用戶是用戶他們本身本人算法
訪問控制的過程,即肯定「誰」訪問「什麼」spring
管理用戶特定的會話,在 Shiro 裏面能夠發現全部的用戶的會話信息都會由 Shiro 來進行控制sql
在對數據源使用加密算法加密的同時,保證易於使用數據庫
Spring Boot 1.5.9
MySQL 5.7
Maven 3.5.2
Spring Data Jpa
Lombokapache
這裏只給出主要的 Shiro 依賴安全
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.4.0-RC2</version> </dependency>
咱們暫時只須要用戶表、角色表,在 Spring boot 中修改配置文件將自動爲咱們建立數據庫表架構
server: port: 8888 spring: datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: root url: jdbc:mysql://localhost:3306/shiro?characterEncoding=utf-8&useSSL=false jpa: generate-ddl: true hibernate: ddl-auto: update show-sql: true
@Data @Entity public class Role { @Id @GeneratedValue private Integer id; private Long userId; private String role; }
@Data @Entity public class User { @Id @GeneratedValue private Long id; private String username; private String password; }
首先創建 Realm 類,繼承自 AuthorizingRealm,自定義咱們本身的受權和認證的方法。Realm 是能夠訪問特定於應用程序的安全性數據(如用戶,角色和權限)的組件。
public class Realm extends AuthorizingRealm { @Autowired private UserService userService; //受權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //從憑證中得到用戶名 String username = (String) SecurityUtils.getSubject().getPrincipal(); //根據用戶名查詢用戶對象 User user = userService.getUserByUserName(username); //查詢用戶擁有的角色 List<Role> list = roleService.findByUserId(user.getId()); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); for (Role role : list) { //賦予用戶角色 info.addStringPermission(role.getRole()); } return info; } //認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //得到當前用戶的用戶名 String username = (String) authenticationToken.getPrincipal(); //從數據庫中根據用戶名查找用戶 User user = userService.getUserByUserName(username); if (userService.getUserByUserName(username) == null) { throw new UnknownAccountException( "沒有在本系統中找到對應的用戶信息。"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName()); return info; } }
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //如下是過濾鏈,按順序過濾,因此/**須要放最後 //開放的靜態資源 filterChainDefinitionMap.put("/favicon.ico", "anon");//網站圖標 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(myRealm()); return defaultWebSecurityManager; } @Bean public MyRealm myRealm() { MyRealm myRealm = new MyRealm(); return myRealm; } }
@Controller public class UserController { @Autowired private UserService userService; @GetMapping("/") public String index() { return "index"; } @GetMapping("/login") public String toLogin() { return "login"; } @GetMapping("/admin") public String admin() { return "admin"; } @PostMapping("/login") public String doLogin(String username, String password) { UsernamePasswordToken token = new UsernamePasswordToken(username, password); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); } catch (Exception e) { e.printStackTrace(); } return "redirect:admin"; } @GetMapping("/home") public String home() { Subject subject = SecurityUtils.getSubject(); try { subject.checkPermission("admin"); } catch (UnauthorizedException exception) { System.out.println("沒有足夠的權限"); } return "home"; } @GetMapping("/logout") public String logout() { return "index"; } }
@Service public class UserService { @Autowired private UserDao userDao; public User getUserByUserName(String username) { return userDao.findByUsername(username); } @RequiresRoles("admin") public void send() { System.out.println("我如今擁有角色admin,能夠執行本條語句"); } }
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"/> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <form action="/login" method="post"> <input type="text" name="username" /> <input type="password" name="password" /> <input type="submit" value="登陸" /> </form> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"/> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> home </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"/> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> index <a href="/login">請登陸</a> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"/> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <form action="/login" method="post"> <input type="text" name="username" /> <input type="password" name="password" /> <input type="submit" value="登陸" /> </form> </body> </html>
這個小案例實現了根據角色來控制用戶訪問,其中最重要的就是 Realm,它充當了Shiro與應用安全數據間的「橋樑」或者「鏈接器」