【Shiro學習筆記】一、Shiro具體使用(基於springboot2.x,前後端分離)
PS:歡迎轉載,但請註明出處,謝謝配合。
Shiro具體使用(基於springboot2.x,前後端分離)
一、前言
基於springboot2.x,前後端分離
二、具體步驟
1、加入shiro依賴包
修改pom.xml
<!-- 引入 shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
2、自定義認證過濾器FormAuthenticationFilter
目的:將原來登入校驗不通過的重定向改為返回Json資料
做法:繼承 authc 的預設認證過濾器(FormAuthenticationFilter)
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
1)重寫onAccessDenied 方法
針對shiro配置認證規則為“authc”的那些url,當校驗出使用者沒有登入的話,執行該方法
/**
* 當shiro校驗使用者未登入時,返回JSON資料(代替原有的跳轉到登入介面)
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
this.log.error("使用者未登入,拒絕訪問");
response.setContentType("application/json") ;
response.setCharacterEncoding("UTF-8") ;
PrintWriter out = response.getWriter() ;
String resultJson = JSON.toJSONString(ResultUtil.error("IMS0000","登入已失效,請重新登入!")) ;
out.write(resultJson) ;
out.flush() ;
out.close() ;
return false ;
}
3、自定義授權過濾器RolesAuthorizationFilter
目的:將原來許可權校驗不通過的重定向改為返回Json資料
做法:繼承 roles 的預設授權過濾器(RolesAuthorizationFilter)
public class MyRolesAuthorizationFilter extends RolesAuthorizationFilter{
1)重寫 onAccessDenied 方法
針對shiro配置授權規則為“roles”的那些url,當校驗出使用者沒有對應許可權的話,執行該方法。
返回json資料格式,由前端決定授權拒絕的後續邏輯。
寫法參考上面的自定義認證過濾器MyFormAuthenticationFilter。
2)重寫 isAccessAllowed 方法(可選)
若role配置多個,預設情況下,是必須多個role同時具備,才有訪問許可權。但一般實際情況,是隻要擁有配置中的某個許可權,即可訪問。因此可以重寫isAccessAllowed 方法,將父類中的“and”邏輯改為“or”邏輯。
// 原邏輯,多個role必須同時滿足,才有許可權訪問
// return subject.hasAllRoles(roles);
// 修改後邏輯 -- begin (多個role之間是“或”關係,只有一個滿足即可)
for(String role: roles) {
if(subject.hasRole(role)) {
return true ;
}
}
return false ;
// 修改後邏輯 --end
4、編寫shiro配置類
1)編寫一個shiro的配置類
@Configuration
public class ShiroConfig {
2)配置類中,定義shiro的過濾器工廠bean
在該過濾器工廠中,可以 設定自定義過濾器、配置認證規則、配置授權規則
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
// 建立 ShiroFilterFactoryBean
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 1、建立過濾器Map,用來裝自定義過濾器
LinkedHashMap<String, Filter> map = new LinkedHashMap<>();
// 2、將自定義過濾器放入map中,如果實現了自定義授權過濾器,那就必須在這裡註冊,否則Shiro不會使用自定義的授權過濾器
map.put("authc", new MyFormAuthenticationFilter()); // 認證過濾器
map.put("roles", new MyRolesAuthorizationFilter()); // 授權過濾器
// 3、將過濾器繫結到shiroFilterFactoryBean上
shiroFilterFactoryBean.setFilters(map);
// 4.配置認證規則 // <!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login/**", "anon"); // 類似/login/xxx請求,可以直接訪問
filterChainDefinitionMap.put("/jnl/**", "anon"); // 類似/jnl/xxx請求,可以直接訪問
// 5.配置授權規則
filterChainDefinitionMap.put("/admin/**", "roles[admin]"); // 類似/admin/**請求,需要admin角色才可訪問(可以寫多個role,預設是多個role同時滿足,可以自定義改成或關係)
//注意:下面這行程式碼必須放在所有許可權設定的最後,不然會導致所有 url 都被攔截,都需要認證(因為filterChainDefinitions 配置過濾規則,是從上到下的順序匹配)
filterChainDefinitionMap.put("/**", "authc"); // 剩下的其他請求,需要登入後才可訪問
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
3)配置類中,定義許可權管理bean
主要包裝我們編寫的業務Realm,該bean會注給上面的過濾器工廠。shiro過濾器攔截的請求,會通過SecurityManager,走到業務Realm的相關方法。
@Bean
public SecurityManager securityManager(UserRealm myShiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm);
return securityManager;
}
4)配置類中,定義業務Realm bean
下面程式碼中的兩個引數,是我專案中的資料庫操作Service,大家根據自己的情況實現即可。或者先不用資料庫操作,直接程式碼中模擬資料庫的返回值,先試試Shiro效果,則不用注入下面兩個引數。
@Bean
public UserRealm myShiroRealm(ShiroUserService shiroUserServiceImpl, ShiroUserRoleService shiroUserRoleServiceImpl) {
UserRealm userRealm = new UserRealm();
userRealm.setShiroUserService(shiroUserServiceImpl) ;
userRealm.setShiroUserRoleService(shiroUserRoleServiceImpl) ;
return userRealm;
}
5、編寫Realm類
目的:運算元據層,實現使用者的認證和授權
做法:繼承Realm父類 AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
1)實現 doGetAuthenticationInfo(認證方法)
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String userName = upToken.getPrincipal().toString() ;
// 1.根據使用者名稱,查詢使用者資訊
Map userMap = this.shiroUserService.queryUser(userName) ;
// 2.判斷使用者名稱是否存在
if(null == userMap || userMap.isEmpty()) {
return null ; // 這裡返回null後,後面邏輯會報出對應異常(賬號不存在)
}
// 3.根據salt,將前端上送的密碼,加鹽加密後,重新放入原token,以便後續邏輯比對兩個加密後的密碼是否一致【我專案中資料庫儲存的使用者密碼是MD5加鹽加密後的,明文儲存的則不需要這步】
String salt = (String) userMap.get("salt") ; // 密碼加密用的鹽值
char[] oldPassword = upToken.getPassword() ;
upToken.setPassword(this.md5withSalt(String.valueOf(oldPassword), salt).toCharArray());
String password = (String) userMap.get("password") ; //DB中的密碼(加密後的)
// 4.根據查詢出的使用者名稱/密碼,構建SimpleAuthenticationInfo認證物件(第一個引數: 使用者名稱 ;第二個引數:DB中查到的密碼 ; 第三個引數:當前Realm名字)
return new SimpleAuthenticationInfo(userName, password, this.getClass().getName()) ; //shiro後續會根據該物件,與原上送引數token物件,比對兩者的密碼是否一致,不一致則丟擲IncorrectCredentialsException異常
}
登入交易中,會呼叫Subject.login(token),最終會執行到Realm 的 doGetAuthenticationInfo 方法。
2)實現 doGetAuthorizationInfo(授權方法)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 1. 獲取授權的使用者
String userName = principals.getPrimaryPrincipal().toString() ;
// 2. 根據使用者名稱,從DB中查詢擁有的角色(另:實際可進行快取優化,不應每次都查庫)
Set<String> roles = this.shiroUserRoleService.queryUserRoles(userName) ;
// 3. 根據角色集合Set<String>,構建SimpleAuthorizationInfo授權物件
return new SimpleAuthorizationInfo(roles);
}
shiro過濾器攔截請求後,若對應了role規則配置的路徑,則會判斷是否具有role角色許可權,通過RolesAuthorizationFilter的 isAccessAllowed方法,最終會執行到Realm 的 doGetAuthorizationInfo 方法。
6、編寫登入Controller
登入交易中,呼叫shiro相關類進行認證,主要包括如下內容:
Subject subject = SecurityUtils.getSubject() ; // 獲取Subject-使用者主體(會把操作交給SecurityManager,最後到Realm)
AuthenticationToken token = new UsernamePasswordToken(userName, passWord) ; // 將從獲取的使用者名稱和密碼設定到一個token中
subject.login(token); // 通過捕獲該方法丟擲的異常,返回對應報錯資訊給前端(UnknownAccountException-使用者名稱錯誤,IncorrectCredentialsException-密碼錯誤)
相關文章
- spring shiro+cas 前後端分離Spring後端
- shiro 學習筆記筆記
- Shiro學習筆記(一) 基本概念與使用筆記
- 在前後端分離專案中使用SpringBoot整合Shiro後端Spring Boot
- Springboot+shiro+mybatis-plus+vue前後端分離專案設計架構Spring BootMyBatisVue後端架構
- 前後端分離——使用OSS後端
- 基於 Node.js 前後端分離的一點思考Node.js後端
- 推薦一款前後端分離的學習專案-基於springboot+vue後端Spring BootVue
- 圖解基於 Node.js 實現前後端分離圖解Node.js後端
- node-vue前後端分離記錄Vue後端
- 再談前後端分離後端
- 前後端分離那些事後端
- 淺談前後端分離後端
- 基於 webpack 的前後端分離開發環境實踐Web後端開發環境
- 基於 TP6.0 和 vue 開發前後端分離管理後臺Vue後端
- SpringBoot整合Shiro+MD5+Salt+Redis實現認證和動態許可權管理|前後端分離(下)----築基後期Spring BootRedis後端
- 《跟我學Shiro》學習筆記 第四章:編碼/加密筆記加密
- laravel框架學習之路(一)前後臺使用者認證分離Laravel框架
- 前後端分離後的前端時代後端前端
- 前後端分離後模組開發後端
- 基於 hyperf,vuetify,casbin 開發的前後端分離管理系統Vue後端
- 基於 Django 和 Vue 前後端分離介面自動化平臺DjangoVue後端
- [造輪子]基於java+typescript 前後端分離專案骨架JavaTypeScript後端
- SpringBoot+vue 前後端的分離專案筆記 [一] 專案搭建Spring BootVue後端筆記
- 關於前後端分離及初始化配置後端
- 零基礎搭建前後端分離專案後端
- ???前後端分離模式的思考???後端模式
- 前後端分離——資料mock後端Mock
- vue前後端分離修改webpackVue後端Web
- 前後端分離整合SpringSecurity後端SpringGse
- 前後端分離,最佳實踐後端
- 淺談WEB前後端分離Web後端
- 什麼是前後端分離?後端
- 前後端分離實踐有感後端
- 從MVC到前後端分離MVC後端
- 前後端分離Ajax入門後端
- 基於 Hyperf 開發的前後分離管理後臺
- Shiro許可權管理框架(一):Shiro的基本使用框架