Java研學-Shiro安全框架(四)

六 SpringBoot集成Shiro認證

1 分析

??Shiro提供認證授權功能,所以SpringBoot中不需再編寫自定義注解,權限攔截,登錄攔截,登錄登出。Shiro 環境中有三個封裝對象Subject ,SecurityManager和Realms,SpringBoot 集成 Shiro 時需要配置相對應的Bean(Subject 不用)
shiro認證

2 導入依賴

<properties><java.version>8</java.version><shiro.version>1.7.1</shiro.version><thymeleaf.extras.shiro.version>2.0.0</thymeleaf.extras.shiro.version>
</properties>
<!--Shiro核心框架 -->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version>
</dependency>
<!-- Shiro使用Spring框架 -->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version>
</dependency>
<!-- Shiro使用EhCache緩存框架 -->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version>
</dependency>
<!-- thymeleaf模板引擎和shiro框架的整合 -->
<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>${thymeleaf.extras.shiro.version}</version>
</dependency>

3 創建數據源

// 有類才能生成Bean
public class EmployeeRealm extends AuthorizingRealm {@Autowiredprivate IEmployeeService employeeService;@Autowiredprivate IPermissionService permissionService;@Autowiredprivate IRoleService roleService;//授權方法@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {Employee currentEmployee= (Employee) principalCollection.getPrimaryPrincipal();SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();if(currentEmployee.isAdmin()){List<Role> roles=roleService.listAll();for(Role role:roles){info.addRole(role.getSn());}info.addStringPermission("*:*");}else{List<Role> roleList=roleService.queryByEmployeeId(currentEmployee.getId());for(Role role:roleList){info.addRole(role.getSn());}//查詢該用戶的權限集合List<String> permissionList=permissionService.queryByEmployeeId(currentEmployee.getId());info.addStringPermissions(permissionList);}return info;}//認證方法@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 根據token獲取用戶名String username = (String) authenticationToken.getPrincipal();// 根據用戶名查詢用戶Employee currentEmployee=employeeService.getByUsername(username);// 根據查詢結果返回對應數據if(currentEmployee==null){return null;}return new SimpleAuthenticationInfo(currentEmployee,currentEmployee.getPassword(), ByteSource.Util.bytes(currentEmployee.getSalt()),getName());}
}

4 創建Shiro配置類

// 配置類注解
@Configuration
public class ShiroConfig {// 1.Realm 數據源從數據庫中查詢數據(先有Realm才能配置Bean,配置這個Bean需要先有這個類)// Bean一定是對象,對象不一定是Bean,對象需要基于類創建@Beanpublic EmployeeRealm employeeRealm(){EmployeeRealm realm = new EmployeeRealm();return realm;}// 2.SecurityManager 安全管理器(基于web環境下的)// 此處可用set調本類方法或傳參的方式聯系Realm    // 傳參是在spring容器中查找這個Bean先類型再名字,去掉@Bean注解會報錯(參數名與方法名盡量一致)// 調用方法首先不會運行該方法,會看方法的返回值類型,在容器中查找該類型,找到多個再按照名字去找,找到了就直接用// 不會運行該方法,若在容器中沒找到該方法,就運行該方法并把返回值放到容器中,然后再拿過來用(無@Bean注解也行)@Beanpublic DefaultWebSecurityManager defaultWebSecurityManager(EmployeeRealm employeeRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(employeeRealm);return securityManager;}// 請求攔截器 shiro過濾器 由于創建麻煩此處使用工廠類創建過濾器對象// 若想知道一個工廠類返回什么類型的Bean 可查詢其getObject()方法@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();// 配置登錄頁面shiroFilterFactoryBean.setLoginUrl("/static/login.html");// 配置安全管理器shiroFilterFactoryBean.setSecurityManager(securityManager);// 配置攔截規則(過濾鏈) 底層為雙鏈表組成(有序,就是我們放入的順序,過濾器根據順序執行)LinkedHashMap<String,String> filterChainDefinitionMap=new LinkedHashMap<>();// 對靜態資源設置匿名訪問(瀏覽器圖標 html css js)放行filterChainDefinitionMap.put("/favicon.ico**","anon");filterChainDefinitionMap.put("/static/**","anon");// 不需攔截的訪問(公共資源)放行filterChainDefinitionMap.put("/login","anon");// 退出并且shiro清除session信息(上下文對象也就是用戶信息) 執行退出方法// 無需在再編寫退出方法,直接調用logout即可踢回登陸頁面filterChainDefinitionMap.put("/logout","logout");// 進行攔截filterChainDefinitionMap.put("/**","authc");// 將攔截規則設置給攔截器鏈(shiro生成了很多攔截器,看我們選用哪個)shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);// 此時無登錄信息 訪問任何頁面都應該被踢回登錄頁面return shiroFilterFactoryBean;}
}

5 LoginController – 登錄方法

??登錄認證實際上是shiro在做,但數據需要在realm中提供

@Controller
public class LoginController {@RequestMapping("/login")@ResponseBodypublic JsonResult login(String username, String password){// 他會自動從Spring容器中拿到SecurityManager設置給SecurityUtils// 然后再將SecurityManager設置給subjectSubject subject = SecurityUtils.getSubject();// 將用戶名密碼封裝到tokenUsernamePasswordToken token = new UsernamePasswordToken(username,password);// 此處返回異常不精確到某一項,防止有人試錯(先試帳號再試密碼)try {// 登錄失敗就拋異常subject.login(token);subject.getSession().setAttribute("user_in_session",subject.getPrincipal());} catch (UnknownAccountException e) {return new JsonResult(false,"賬號密碼有誤");} catch (IncorrectCredentialsException e) {return new JsonResult(false,"帳號密碼有誤");} catch (Exception e) {return new JsonResult(false,"系統異常,稍后再試");}return new JsonResult(true,"登錄成功");}
}

6 數據源查詢方法(service) – getByUsername

// IEmployeeService
Employee getByUsername(String username);
// EmployeeServiceImpl
public Employee getByUsername(String username) {return employeeMapper.getByUsername(username);
}

7 數據源查詢方法(mapper) – getByUsername

// mapper
Employee getByUsername(String username);
// xml
<select id="getByUsername" resultMap="BaseResultMap">select e.id, e.username, e.name, e.password, e.email, e.age, e.admin,d.id d_id,d.name d_name,d.sn d_sn,e.saltfrom employee e left join department d on e.dept_id = d.idwhere username=#{username}
</select>

8 shiro 內置過濾器

??shiro 啟動時會默認將以下這些類(過濾器)加載到程序中,然后使用Map將這些數據的關系以key value的方式存儲起來。

過濾器的名稱(key)Java 類(value)
anonorg.apache.shiro.web. lter.authc.AnonymousFilter
authcorg.apache.shiro.web. lter.authc.FormAuthenticationFilter
authcBasicorg.apache.shiro.web. lter.authc.BasicHttpAuthenticationFilter
rolesorg.apache.shiro.web. lter.authz.RolesAuthorizationFilter
permsorg.apache.shiro.web. lter.authz.PermissionsAuthorizationFilter
userorg.apache.shiro.web. lter.authc.UserFilter
logoutorg.apache.shiro.web. lter.authc.LogoutFilter
portorg.apache.shiro.web. lter.authz.PortFilter
restorg.apache.shiro.web. lter.authz.HttpMethodPermissionFilter
sslorg.apache.shiro.web. lter.authz.SslFilter

anon: 匿名攔截器,即不需要登錄即可訪問(誰都可以訪問,不需要攔截);一般用于靜態資源過濾;示例“/static/**=anon”

authc: 表示需要認證(登錄)才能使用,該路徑所有請求都需登錄后才能訪問;示例“/**=authc”

authcBasic:Basic HTTP身份驗證攔截器

roles: 角色授權攔截器,驗證用戶是否擁有資源角色;示例“/admin/**=roles[admin]”

perms: 權限授權攔截器,驗證用戶是否擁有資源權限;示例“/user/create=perms[“user:create”]”

user: 用戶攔截器,用戶已經身份驗證/記住我登錄的都可;示例“/index=user”

logout: 退出攔截器,登出后自動清理session中的用戶信息。主要屬性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout”

port: 端口攔截器,主要屬性:port(80):可以通過的端口;示例“/test= port[80]”,如果用戶訪問該頁面是非80,將自動將請求端口改為80并重定向到該80端口,其他路徑/參數等都一樣

rest: rest風格攔截器;

ssl: SSL攔截器,只有請求協議是https才能通過;否則自動跳轉會https端口(443);其他和port攔截器一樣;

9 400錯誤問題解決

??當傳入的參數 SpringMVC 無法轉換時,就會出現400問題(第一次訪問時出現),session是在瀏覽器第一次訪問服務器時,由服務器創建并生成一個sessionID,通過response響應給瀏覽器。

??瀏覽器訪問服務器時,服務器中有一個session池,當找到session后,將返回對應的JsessionID。

??但第一次訪問時會首先經過shiro過濾器,其中有一個會話管理器(SessionManager),他發現當前是第一次訪問,因此會進行一次URL重寫,服務器會生成session并把sessionID返回給安全管理器,會話管理器通過重定向回到瀏覽器,再次發起申請訪問服務器,此時第一次訪問服務器實際上是沒有訪問到服務器,因此服務器無法接收攜帶的參數(JsessionID)報400錯誤。(去掉url中的JsessionID即可訪問)

??第二次訪問時,session已經存在,通過id尋找session,可以正常訪問。或者告訴會話管理器,不需要做url重寫,可在shiroconfig中生成會話管理器,將 setSessionIdUrlRewritingEnabled 設置為false即可(默認為true)。

@Configuration
public class ShiroConfig {// 略// 會話管理器@Beanpublic DefaultWebSecurityManager sessionManager(){DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();// url重寫開關sessionManager.setSessionIdUrlRewritingEnabled(false);return sessionManager;}
}

??此處需注意,編寫好 sessionManager 會話管理器后,需要將其設置給 SecurityManager 安全管理器(通過參數)

@Configuration
public class ShiroConfig {// 略@Bean//安全管理器public DefaultWebSecurityManager defaultWebSecurityManager(EmployeeRealm employeeRealm, DefaultWebSessionManager sessionManager){// 創建安全管理器DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();// 設置realmsecurityManager.setRealm(employeeRealm);// 設置會話管理器securityManager.setSessionManager(sessionManager);return securityManager;}//會話管理器@Beanpublic DefaultWebSecurityManager sessionManager(){DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();sessionManager.setSessionIdUrlRewritingEnabled(false);return sessionManager;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/44838.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/44838.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/44838.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java核心技術【二十一】Java的I/O流處理:文件的讀寫操作

Java的I/O流處理&#xff1a;文件讀寫操作 【創作】 不易&#xff0c;【點贊】 是情義&#xff0c;【關注】 是動力&#xff0c;【收藏】 是回憶。 示例代碼地址&#xff1a;https://gitee.com/code-in-java/csdn-blog.git 在Java編程中&#xff0c;輸入/輸出&#xff08;I/O&a…

PyTorch實現BERT預訓練模型轉化指南

huggingface官方的介紹&#xff1a; https://huggingface.co/transformers/converting_tensorflow_models.html 直接用命令行 把箭頭處路徑改為自己放原有tf版本預訓練模型的路徑 回車后會有一大堆提示&#xff0c;然后發現路徑下多了一個bin文件&#xff0c;加上原本的config…

順序結構 ( 六 ) —— 順序結構實例 【互三互三】

&#x1f680;歡迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e;&#x1f680;所屬專欄&#xff1a;C教程&#x1f48e; &#x1f680;關注博主&#xff0c;后期持續更新系列文章 &#x1f680;如果有錯誤感謝請大家批評指出&#xff0c;及時修改 &am…

iNavFlight飛控固件學習-1《開發環境搭建》

目錄 文章目錄 目錄摘要1.官網2.形成Linux開發環境工具2.1 簡介2.2 相關工具2.2.1 Ubuntu / Debian系統配置命令2.2.2 Fedora系統配置命令2.2.3 Fedora系統配置命令 2.3 克隆存儲庫2.4 構建工具2.5 使用cmake2.6 構建固件2.7 清除2.8 cmake 緩存維護2.9 編譯通過ninja2.10 更新…

js 日期比較大小

在JavaScript中&#xff0c;比較日期大小通常涉及將日期轉換為時間戳&#xff08;自1970年1月1日以來的毫秒數&#xff09;&#xff0c;然后比較這些時間戳。這是因為直接比較兩個Date對象可能不會按預期工作&#xff0c;特別是如果你試圖了解哪個日期在另一個日期之前或之后。…

紅酒與未來科技:傳統與創新的碰撞

在歲月的長河中&#xff0c;紅酒以其深邃的色澤、豐富的口感和不同的文化魅力&#xff0c;成為人類文明中的一顆璀璨明珠。而未來科技&#xff0c;則以其迅猛的發展速度和無限的可能性&#xff0c;領著人類走向一個嶄新的時代。當紅酒與未來科技相遇&#xff0c;一場傳統與創新…

2024.07.03校招 實習 內推 面經

綠*泡*泡VX&#xff1a; neituijunsir 交流*裙 &#xff0c;內推/實習/校招匯總表格 1、提前批 | 中國兵器工業集團第二〇二研究所 | 提前批/招/聘暨/暑期/開放日 提前批 | 中國兵器工業集團第二〇二研究所 | 提前批招聘暨暑期開放日 2、夏令營 | 2024年南網數字集團“未來…

ADI新型充電器解決方案可實現電池堆電壓和充電效率

就目前而言&#xff0c;這可能是生活中zui常見的問題了。世紀之交&#xff0c;電池&#xff08;尤其是基于鋰離子的電池&#xff09;成本的降低和性能的提高&#xff0c;推動了電池供電的儲能和便攜式設備的穩步增長。此外&#xff0c;超級電容器由于具有獨特的性質&#xff0c…

oppo25屆秋招,快手25屆技術人才專項計劃內推

oppo25屆秋招&#xff0c;快手25屆技術人才專項計劃內推 ①【OPPO】25屆秋招開啟&#xff01; 內推簡歷優先篩選&#xff01; &#x1f449;崗位類別&#xff1a;AI/算法類&#xff0c;軟件類&#xff0c;硬件類&#xff0c;工程技術類&#xff0c;品牌策劃類&#xff0c;設計類…

骨傳導耳機最熱門好用款推薦,選購骨傳導耳機前不能忽略的六大細節

如今的社會在耳機種類方面可以說是越來越多&#xff0c;于是很多人在挑選的時候往往選擇不到適合自己的一款耳機&#xff0c;尤其是在近些年來席卷耳機市場的骨傳導耳機&#xff0c;開放耳道的設計在很多時候佩戴無異于是更加的適合&#xff0c;正好小編這邊對于比較熱門的幾款…

社交App iOS審核中的4.3問題:深入分析與解決策略

社交App審核中的4.3問題&#xff1a;深入分析與解決策略 在iOS應用開發和審核過程中&#xff0c;開發者經常會遇到蘋果審核4.3問題。這一問題往往涉及應用的設計和內容重復性&#xff0c;導致應用被拒絕上架。為了幫助開發者更好地理解和解決這一問題&#xff0c;本文將對4.3問…

動漫3d模型設計需要注意什么?---模大獅模型網

設計動漫3D模型時&#xff0c;有幾個方面需要注意&#xff1a; 保持角色風格一致性&#xff1a; 動漫通常有獨特的風格和美學&#xff0c;設計時要確保模型與所代表的角色或作品的整體風格相符。注意保持線條和比例的一致性&#xff0c;使模型能夠忠實地呈現原作的特點。 注重…

springboot餐飲管理系統-計算機畢業設計源碼43667

摘 要 在信息化、數字化的時代背景下&#xff0c;餐飲行業面臨著前所未有的挑戰與機遇。為了提高運營效率、優化顧客體驗&#xff0c;餐飲企業亟需一套高效、穩定且靈活的管理系統來支撐其日常運營。基于Spring Boot的餐飲管理系統應運而生&#xff0c;成為餐飲行業數字化轉型的…

Python基礎教學之一:入門篇——邁入編程世界的第一步

Python基礎教學之一&#xff1a;入門篇——邁入編程世界的第一步 一、Python簡介&#xff1a;歷史與現狀 Python&#xff0c;一種解釋型、高級和通用的編程語言&#xff0c;由Guido van Rossum在1989年圣誕節期間創造&#xff0c;并于1991年首次發布。設計哲學強調代碼的可讀性…

論文降痕降重全攻略:從技巧到工具,助你輕松應對學術挑戰

AIGC降重工具&#xff1a;快速降低論文查重率 高查重率是許多畢業生的困擾。通常&#xff0c;高查重率源于過度引用未經修改的參考資料和格式錯誤。傳統的降重方法&#xff0c;如修改文本和增添原創內容&#xff0c;雖必要但耗時且成效不一。 鑒于此&#xff0c;應用AI工具進…

【企業級監控】源碼部署Zabbix與監控主機

Zabbix企業級分布式監控 文章目錄 Zabbix企業級分布式監控資源列表基礎環境一、LNMP環境搭建&#xff08;在zbx主機上&#xff09;1.1、配置Yum倉庫1.1.1、下載阿里云的倉庫文件1.2.2、安裝PHP7的倉庫1.2.3、生成Mariadb10.11的倉庫文件1.2.4、快速重建Yum緩存 1.2、安裝PHP7.4…

請求取消(多種)

XMLHttpRequest XMLHttpRequest 可以使用 abort() 方法來實現請求取消。 const xhr new XMLHttpRequest();xhr.open(GET, http://localhost:3000/api/txt);xhr.timeout 2;xhr.ontimeout function () {xhr.abort();console.error(請求超時&#xff0c;已中斷);};xhr.onload …

shell腳本之for循環

一、循環中的常見語句 1.1 echo 打印 -n &#xff1a;表示不換行輸出-e &#xff1a;輸出轉義字符 \b &#xff1a;相當于退格鍵(backspace)\n &#xff1a;換行&#xff0c;相當于回車\f &#xff1a;換行&#xff0c;換行后的新行的開頭連著上一行的行尾\t &#xff1a;插入…

編程用什么軸體比較好用:全面解析四種軸體的優劣與應用

編程用什么軸體比較好用&#xff1a;全面解析四種軸體的優劣與應用 在編程的世界里&#xff0c;選擇一款合適的機械鍵盤軸體&#xff0c;不僅能提升工作效率&#xff0c;更能帶來舒適的打字體驗。然而&#xff0c;面對市場上琳瑯滿目的軸體類型&#xff0c;究竟該如何選擇呢&a…

a+=1和a=a+1的區別

文章目錄 a1 和a a1的區別一、實例代碼二、代碼解釋三、總結 a1 和a a1的區別 一、實例代碼 public class Test {public static void main(String[] args) {byte a 10; // a a 1; // a (byte) (a 1);a 1;System.out.println(a);} }上面的對變量a進行加一操作時&a…