SpringBoot整合Shiro權限管理
Shiro(Java安全架構)
什麼是權限管理
權限管理包括使用者身份認證和授權兩部分,簡稱認證授權。對于需要通路控制的資源使用者首先經過身份認證,認證通過後使用者具有該資源的通路權限方可通路。
1.什麼是Shiro
Shiro是一個強大且易用的Java安全架構,執行身份驗證、授權、密碼和會話管理。
1.三個核心元件:Subject, SecurityManager 和 Realms.
Subject:即“目前操作使用者”。但是,在Shiro中,Subject這一概念并不僅僅指人,也可以是第三方程序、背景帳戶(Daemon Account)或其他類似事物。它僅僅意味着“目前跟軟體互動的東西”。
Subject代表了目前使用者的安全操作,SecurityManager則管理所有使用者的安全操作。
SecurityManager:它是Shiro架構的核心,典型的Facade模式,Shiro通過SecurityManager來管理内部元件執行個體,并通過它來提供安全管理的各種服務。
Realm: Realm充當了Shiro與應用安全資料間的“橋梁”或者“連接配接器”。也就是說,當對使用者執行認證(登入)和授權(通路控制)驗證時,Shiro會從應用配置的Realm中查找使用者及其權限資訊。
2.Shiro四大核心功能:Authentication,Authorization,Cryptography,Session Management
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPn5keVpmTzcGROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5ITNzUTNykDM4ITMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
3.使用者名密碼認證流程
4.授權流程
先建立一個SpringBoot項目,添加shiro的相關依賴
<!--添加 shiro 依賴-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
修改配置檔案application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/project?serverTimezone=UTC
username: root
password: 123456
thymeleaf:
cache: false
encoding: utf-8
prefix: classpath:/templates/
mybatis:
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.shiro.login.domain
實體類
使用者
@Data
public class User {
private String id;
private String name;
private String password;
}
角色
@Data
public class Role {
private String id;
private String name;
}
權限
@Data
public class Permission {
private String id;
private String name;
private String url;
}
Mapper.xml和Service層就不貼了
ShiroConfiguration
package com.shiro.login.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
/**
* @Author: Adorez
* @Date: 2019/11/27 18:00
* @Description:
*/
@Configuration
public class ShiroConfiguration {
@Bean("userRealm")
public UserRealm userRealm(){
UserRealm userRealm=new UserRealm();
return userRealm;
}
/**
* 定義安全管理器 securityManager
* @return
*/
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
manager.setRealm(userRealm);
return manager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
//設定securityManager
bean.setSecurityManager(securityManager);
//設定登入頁面
bean.setLoginUrl("/login");
//設定登入成功跳轉的頁面
bean.setSuccessUrl("/success");
bean.setUnauthorizedUrl("/error/unAuth");
//定義過濾器
LinkedHashMap<String,String> filter=new LinkedHashMap<>();
filter.put("/","authc");//首頁需要認證
filter.put("/login","anon");//登入頁面無需認證
filter.put("/toLogin","anon");//登入頁面無需認證
filter.put("/vip/index","roles[vip]");//添權重限配置
filter.put("/error/unAuth","authc");
//需要登入通路的資源,一般将/**放在最下邊
filter.put("/**","authc");
bean.setFilterChainDefinitionMap(filter);
return bean;
}
/**
* 配置shiro和spring的關聯
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
/**
* lifecycleBeanPostProcessor 是負責生命周期的 初始化和銷毀的類
* @return
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* Spring 的一個 bean , 由 Advisor 決定對哪些類的方法進行 AOP 代理 .
* @return
*/
@Bean
public static DefaultAdvisorAutoProxyCreator
getDefaultAdvisorAutoProxyCreator(){
return new DefaultAdvisorAutoProxyCreator();
}
/**
* 頁面上使用shiro标簽
* @return
*/
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
UserRealm
package com.shiro.login.config;
import com.shiro.login.domain.Permission;
import com.shiro.login.domain.Role;
import com.shiro.login.domain.User;
import com.shiro.login.service.PermissionService;
import com.shiro.login.service.RoleService;
import com.shiro.login.service.UserService;
import org.apache.shiro.authc.*;
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 org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* @Author: Adorez
* @Date: 2019/11/27 18:13
* @Description:
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private PermissionService permissionService;
/**
* 為使用者授權
* 角色權限和對應的權限添加
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("為使用者授權");
//擷取登入使用者名
User userInfo = (User) principalCollection.getPrimaryPrincipal();
//查詢使用者名稱
User user = userService.findUserByuserName(userInfo.getName());
//添加角色和權限
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
//根據查詢出來的使用者資訊獲得角色
Role role = roleService.findById(Integer.valueOf(user.getId()));
//添加角色1
simpleAuthorizationInfo.addRole(role.getName());
//根據查詢出來的角色查詢權限
List<Permission> perList = permissionService.findById(Integer.valueOf(role.getId()));
for (Permission per:perList) {
//添權重限
simpleAuthorizationInfo.addStringPermission(per.getName());
}
return simpleAuthorizationInfo;
}
/**
* 認證登入
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("---認證登入");
//token攜帶了使用者資訊
UsernamePasswordToken usernamePasswordToken=(UsernamePasswordToken)token;
//擷取前端輸入的使用者名
String username = usernamePasswordToken.getUsername();
//根據使用者名查詢資料庫中對應的記錄
User user = userService.findUserByuserName(username);
if (null==user) {
new UnknownAccountException("賬戶不存在");
}else {
new IncorrectCredentialsException("密碼不正确");
}
//目前realm對象的name
String realmName = getName();
//鹽值
ByteSource source=ByteSource.Util.bytes(user.getName());
/**
* 封裝使用者資訊,建構authenticationInfo對象并傳回
* 參數一:使用者對象
* 參數二:密碼
* 參數三:鹽值
* 參數四:realm的名字
*/
AuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,user.getPassword(),source,realmName);
return authenticationInfo;
}
}
Controller
package com.shiro.login.controller;
import com.shiro.login.domain.User;
import com.shiro.login.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpSession;
/**
* @Author: Adorez
* @Date: 2019/11/27 17:57
* @Description:
*/
@Controller
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/toLogin")
public String login(User user, HttpSession session){
//通過SecurityUtils.getSubject()可以獲得目前的Subject
Subject subject = SecurityUtils.getSubject();
//判斷是否有登入資訊
if(!subject.isAuthenticated()){
UsernamePasswordToken token=new UsernamePasswordToken(user.getName(),user.getPassword());
try {
//執行認證操作
subject.login(token);
session.setAttribute("user", subject.getPrincipal());//getPrincipal()得到目前登入的使用者名
}catch (Exception e){
System.out.println(e.getStackTrace());
return "redirect:login";
}
}
return "redirect:/";
}
@GetMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
if (subject!=null) {
subject.logout();
}
return "login";
}
}
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登入</title>
</head>
<body>
<h1>使用者登入</h1>
<hr>
<form id="from" th:action="@{/toLogin}" method="post">
<table>
<tr>
<td>使用者名</td>
<td>
<input type="text" name="name" placeholder="請輸入賬戶名"/>
</td>
</tr>
<tr>
<td>密碼</td>
<td>
<input type="password" name="password" placeholder="請輸入密碼"/>
</td>
</tr>
<tr>
<td colspan="2">
<font color="red">[[${msg}]]</font>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="登入"/>
<input type="reset" value="重置"/>
</td>
</tr>
</table>
</form>
</body>
</html>
index.html
<!DOCTYPE html>
<html xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<title>首頁</title>
</head>
<body>
<h1>首頁</h1>
<hr>
<ul>
<li><a href="/user/index">個人中心</a></li>
<shiro:hasRole name="vip"><li><a href="/vip/index">會員中心</a></li></shiro:hasRole>
<li><a href="/test/index">hei</a></li>
<li><a href="logout">登出</a></li>
</ul>
</body>
</html>
error/unAuth.html
<!DOCTYPE html>
<html>
<head>
<title>未授權提示</title>
</head>
<body>
<h1>您還不是<font color="red">會員</font>,沒有權限通路這個頁面!</h1>
</body>
</html>
shiro标簽的使用
guest标簽 | 驗證目前使用者是否為“訪客”,即未認證(包含未記住)的使用者 |
---|---|
user标簽 | 認證通過或已記住的使用者 |
authenticated标簽 | 已認證通過的使用者。不包含已記住的使用者,這是與user标簽的差別所在 |
notAuthenticated | 未認證通過使用者,與authenticated标簽相對應。與guest标簽的差別是,該标簽包含已記住使用者 |
principal | 輸出目前使用者資訊,通常為登入帳号資訊 |
hasRole | 驗證目前使用者是否屬于該角色 |
lacksRole | 與hasRole标簽邏輯相反,當使用者不屬于該角色時驗證通過 |
hasAnyRole | 驗證目前使用者是否屬于以下任意一個角色 |
hasPermission | 驗證目前使用者是否擁有指定權限 |
lacksPermission | 與hasPermission标簽邏輯相反,目前使用者沒有制定權限時,驗證通過 |
項目具體實作
連結:https://pan.baidu.com/s/1RKzOZFU81XgJJ2di7nFpCA
提取碼:qzp4