安全無處不在,趁著放假讀了一下Shiro 文檔,並記錄一下Shiro 整合Spring Boot 在數據庫中根據角色控制訪問權限
簡介
Apache Shiro是一個功能強大、靈活的,開源的安全框架。它可以乾淨利落地處理身份驗證、授權、企業會話管理和加密。
上圖是Shiro 的基本架構
Authentication(認證)
有時被稱為“登錄”,用來證明用戶是用戶他們自己本人
Authorization(授權)
訪問控制的過程,即確定“誰”訪問“什麼”
Session Management(會話管理)
管理用戶特定的會話,在Shiro 裡面可以發現所有的用戶的會話信息都會由Shiro 來進行控制
Cryptography(加密)
在對數據源使用加密算法加密的同時,保證易於使用
Start
環境
Spring Boot 1.5.9 MySQL 5.7 Maven 3.5.2 Spring Data Jpa Lombok
添加依賴
這裡只給出主要的Shiro 依賴
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.4.0-RC2</version> </dependency>
配置
我們暫時只需要用戶表、角色表,在Spring boot 中修改配置文件將自動為我們創建數據庫表
server: port: 8888spring: 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
實體
Role.java
@Data@Entitypublic class Role { @Id @GeneratedValue private Integer id; private Long userId; private String role;}User.java
@Data@Entitypublic class User { @Id @GeneratedValue private Long id; private String username; private String password;}Realm
首先建立Realm 類,繼承自AuthorizingRealm,自定義我們自己的授權和認證的方法。 Realm 是可以訪問特定於應用程序的安全性數據(如用戶,角色和權限)的組件。
Realm.java
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; }}Shiro 配置類
ShiroConfig.java
@Configurationpublic 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; }}控制器
UserController.java
@Controllerpublic 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
UserService.java
@Servicepublic class UserService { @Autowired private UserDao userDao; public User getUserByUserName(String username) { return userDao.findByUsername(username); } @RequiresRoles("admin") public void send() { System.out.println("我現在擁有角色admin,可以執行本條語句"); }}展示層
admin.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>
home.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>
index.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" rel="external nofollow" >請登錄</a></body></html>
login.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與應用安全數據間的“橋樑”或者“連接器”
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。