Springboot集成shiro之登錄攔截/權(quán)限控制方式
一、在pom文件中添加依賴(lài)
<!--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>二、配置類(lèi) shiroConfig--登錄認(rèn)證攔截
1.創(chuàng)建realm配置realm
2.配置安全管理器DefaultWebSecurityManager----核心
3.配置請(qǐng)求過(guò)濾器ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Shiro的核心安全接口,這個(gè)屬性是必須的
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 身份認(rèn)證失敗,則跳轉(zhuǎn)到登錄頁(yè)面的配置
shiroFilterFactoryBean.setLoginUrl("/static/login.html");
// 權(quán)限認(rèn)證失敗,則跳轉(zhuǎn)到指定頁(yè)面
shiroFilterFactoryBean.setUnauthorizedUrl("/nopermissioin");
// Shiro連接約束配置,即過(guò)濾鏈的定義
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 對(duì)靜態(tài)資源設(shè)置匿名訪(fǎng)問(wèn)
// 不需要攔截的訪(fǎng)問(wèn)
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/favicon.ico**", "anon");
filterChainDefinitionMap.put("/static/**", "anon");
?
// 退出 logout地址,shiro去清除session
filterChainDefinitionMap.put("/logout", "logout");
// 所有請(qǐng)求需要認(rèn)證
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}4.登錄控制器,使用subject登錄
Subject subject= SecurityUtils.getSubject();
UsernamePasswordToken token= new UsernamePasswordToken(username, password);
subject.login(token);5.shiro內(nèi)置過(guò)濾器介紹
anon: 匿名攔截器,即不需要登錄即可訪(fǎng)問(wèn);一般用于靜態(tài)資源過(guò)濾;示例“/static/**=anon”authc: 表示需要認(rèn)證(登錄)才能使用;示例“/**=authc”authcBasic:Basic HTTP身份驗(yàn)證攔截器roles: 角色授權(quán)攔截器,驗(yàn)證用戶(hù)是否擁有資源角色;示例“/admin/**=roles[admin]”perms: 權(quán)限授權(quán)攔截器,驗(yàn)證用戶(hù)是否擁有資源權(quán)限;示例“/user/create=perms["user:create"]”user: 用戶(hù)攔截器,用戶(hù)已經(jīng)身份驗(yàn)證/記住我登錄的都可;示例“/index=user”logout: 退出攔截器,主要屬性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout”port: 端口攔截器,主要屬性:port(80):可以通過(guò)的端口;示例“/test= port[80]”,如果用戶(hù)訪(fǎng)問(wèn)該頁(yè)面是非80,將自動(dòng)將請(qǐng)求端口改為80并重定向到該80端口,其他路徑/參數(shù)等都一樣rest: rest風(fēng)格攔截器;ssl: SSL攔截器,只有請(qǐng)求協(xié)議是https才能通過(guò);否則自動(dòng)跳轉(zhuǎn)會(huì)https端口(443);其他和port攔截器一樣;
注:
- anon,authcBasic,auchc,user是認(rèn)證過(guò)濾器,
- perms,roles,ssl,rest,port是授權(quán)過(guò)濾器
三、權(quán)限控制(鑒權(quán)
1.注解式
①在需要權(quán)限的控制器方法上貼注解
- @RequiresPermissions("權(quán)限表達(dá)式") ==> 需要權(quán)限控制
- @RequiresRoles() ==> 需要角色控制
②在配置文件中配置兩個(gè)bean
/**
* 開(kāi)啟Shiro注解通知器
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
DefaultWebSecurityManager securityManager)
{
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 設(shè)置支持CGlib代理
* 詳情看DefaultAopProxyFactory#createAopProxy
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}③在realm中完善鑒權(quán)邏輯
//授權(quán)相關(guān)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Employee employee = (Employee)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//根據(jù)用戶(hù)的id查詢(xún)?cè)撚脩?hù)擁有的角色編碼
List<Role> roles = roleService.queryByEmployeeId(employee.getId());
for(Role role:roles){
info.addRole(role.getSn());
}
//根據(jù)用戶(hù)的id查詢(xún)?cè)撚脩?hù)擁有的權(quán)限表達(dá)式
List<String> permissions = permissionService.queryByEmployeeId(employee.getId());
info.addStringPermissions(permissions);
return info;
}注:可以添加統(tǒng)一異常處理--針對(duì)用戶(hù)沒(méi)有權(quán)限
@ControllerAdvice
public class CommonControllerAdvice {
@ExceptionHandler(AuthorizationException.class)
public String exceptionHandler(AuthorizationException e){
e.printStackTrace();
return "/nopermission";
}
}注--超級(jí)管理員
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){
Employee employee = (Employee) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if (employee.isAdmin()) {//是管理員--權(quán)限可以使用通配符
info.addStringPermission("*:*");
//角色--不能使用通配符
List<Role> roles = roleService.selAll();
for (Role role : roles) {
info.addRole(role.getSn());
}else{
//余下邏輯與普通鑒權(quán)相同
}
return info;
}2.編程式--以查詢(xún)?yōu)槔?/h3>
@RequestMapping("/list")
public String list(Model model, QueryObject qo) {
//獲取subject主體對(duì)象
Subject subject = SecurityUtils.getSubject();
PageInfo<Department> pageInfo = null;
//判斷此subject是否有這個(gè)權(quán)限--我猜測(cè)這個(gè)subject就是從數(shù)據(jù)庫(kù)查出來(lái)的用戶(hù)信息,根據(jù)id和關(guān)聯(lián)表查出的權(quán)限
if(subject.isPermitted("department:list")){
//有權(quán)限執(zhí)行代碼
pageInfo = departmentService.query(qo);
}else{
//沒(méi)有權(quán)限返回空頁(yè)面
pageInfo = new PageInfo<>(Collections.EMPTY_LIST);
}
model.addAttribute("pageInfo", pageInfo);
return "department/list";
}
@RequestMapping("/list")
public String list(Model model, QueryObject qo) {
//獲取subject主體對(duì)象
Subject subject = SecurityUtils.getSubject();
PageInfo<Department> pageInfo = null;
//判斷此subject是否有這個(gè)權(quán)限--我猜測(cè)這個(gè)subject就是從數(shù)據(jù)庫(kù)查出來(lái)的用戶(hù)信息,根據(jù)id和關(guān)聯(lián)表查出的權(quán)限
if(subject.isPermitted("department:list")){
//有權(quán)限執(zhí)行代碼
pageInfo = departmentService.query(qo);
}else{
//沒(méi)有權(quán)限返回空頁(yè)面
pageInfo = new PageInfo<>(Collections.EMPTY_LIST);
}
model.addAttribute("pageInfo", pageInfo);
return "department/list";
}3.標(biāo)簽式--需要配置Shiro集成ThymeLeaf標(biāo)簽支持(了解)
①配置文件中添加依賴(lài)
/**
* thymeleaf模板引擎和shiro框架的整合
*/
@Bean
public ShiroDialect shiroDialect()
{
return new ShiroDialect();
}②頁(yè)面中添加頭
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
③使用
<a href="#" rel="external nofollow" class="btn btn-success btn-input" style="margin: 10px" shiro:hasPermission="department:saveOrUpdate">
<span class="glyphicon glyphicon-plus"></span> 添加
</a>四、其他集成
4.1 ehchche--緩存
1.在resource目錄下新建ehcache/ehcache-shiro.xml文件,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>- 配置屬性說(shuō)明
| 參數(shù) | 說(shuō)明 |
| maxElementsInMemory | 緩存對(duì)象最大個(gè)數(shù) |
| eternal | 對(duì)象是否永久有效,一但設(shè)置了,timeout 將不起作用。 |
| timeToIdleSeconds | 對(duì)象空閑時(shí)間,指對(duì)象在多長(zhǎng)時(shí)間沒(méi)有被訪(fǎng)問(wèn)就會(huì)失效(單位:秒)。僅當(dāng) eternal=false 對(duì)象不是永久有效時(shí)使用,可選屬性,默認(rèn)值是 0,也就是可閑置時(shí)間無(wú)窮大。 |
| timeToLiveSeconds | 對(duì)象存活時(shí)間,指對(duì)象從創(chuàng)建到失效所需要的時(shí)間(單位:秒)。僅當(dāng) eternal=false 對(duì)象不是永久有效時(shí)使用,默認(rèn)是 0,也就是對(duì)象存活時(shí)間無(wú)窮大。 |
| memoryStoreEvictionPolicy | 當(dāng)達(dá)到 maxElementsInMemory 限制時(shí),Ehcache 將會(huì)根據(jù)指定的策略去清理內(nèi)存。 |
- 緩存淘汰策略:
| 策略 | 說(shuō)明 |
| LRU | 默認(rèn),最近最少使用,距離現(xiàn)在最久沒(méi)有使用的元素將被清出緩存 |
| FIFO | 先進(jìn)先出, 如果一個(gè)數(shù)據(jù)最先進(jìn)入緩存中,則應(yīng)該最早淘汰掉 |
| LFU | 較少使用,意思是一直以來(lái)最少被使用的,緩存的元素有一個(gè)hit 屬性(命中率),hit 值最小的將會(huì)被清出緩存 |
2.添加配置
@Bean
public EhCacheManager getEhCacheManager()
{
EhCacheManager em = new EhCacheManager();
em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
return em;
}
/**
* 自定義Realm
*/
@Bean
public EmployeeRealm userRealm(EhCacheManager cacheManager)
{
EmployeeRealm employeeRealm = new EmployeeRealm();
employeeRealm.setCacheManager(cacheManager);
return employeeRealm;
}3.使用自定義的配置
@Bean
//傳遞的參數(shù)名要與自定義realm的bean名一致-userRealm
public DefaultWebSecurityManager securityManager(EmployeeRealm userRealm,DefaultWebSessionManager sessionManager){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
//添加realm對(duì)象
securityManager.setRealm(userRealm);
//添加session管理器對(duì)象
securityManager.setSessionManager(sessionManager);
return securityManager;
}4.2加鹽加密
1.在數(shù)據(jù)庫(kù)和實(shí)體類(lèi)中分別添加salt字段
2.修改對(duì)應(yīng)的mapper映射文件
3.改realm
/**
* 在返回值中添加第三個(gè)參數(shù) ByteSource.Util.bytes(employee.getSalt())
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal();
Employee employee = employeeService.selUserByUsername(username);
if (employee == null) {
return null;
}
return new SimpleAuthenticationInfo(employee, employee.getPassword(),
ByteSource.Util.bytes(employee.getSalt()), getName());
}4.改配置類(lèi)
- 添加憑證匹配器
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher matcher=new HashedCredentialsMatcher("md5");
matcher.setHashIterations(3);
return matcher;
}- 將憑證匹配器設(shè)置到realm中
@Bean
public EmployeeRealm userRealm(EhCacheManager cacheManager,
HashedCredentialsMatcher hashedCredentialsMatcher)
{
EmployeeRealm employeeRealm = new EmployeeRealm();
employeeRealm.setCacheManager(cacheManager);
//這里
employeeRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return employeeRealm;
}5.注冊(cè)功能修改--service
//添加用戶(hù)的service方法
@Override
public void add(Employee employee, Long[] roleIds) {
//生成隨機(jī)鹽
String salt = UUID.randomUUID().toString().substring(0, 4);
//生成加密的密碼
Md5Hash pwd=new Md5Hash(employee.getPassword(), salt,3);
//將鹽和密碼都設(shè)置給對(duì)象
employee.setPassword(pwd.toString());
employee.setSalt(salt);
//最后進(jìn)行添加
employeeMapper.insert(employee);
//向關(guān)聯(lián)表批量插入
if (roleIds != null && roleIds.length > 0 && !employee.isAdmin())//合理化校驗(yàn)
{
employeeMapper.insertRelationBatch(employee.getId(), roleIds);
}
}五、關(guān)于重定向出現(xiàn)404--sessionId
1.在配置類(lèi)中添加session管理器
/**
* session管理器
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionIdUrlRewritingEnabled(false);
return sessionManager;
}2.在安全管理器中添加
//安全管理器對(duì)象
@Bean
public DefaultWebSecurityManager securityManager(EmployeeRealm userRealm,DefaultWebSessionManager sessionManager){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
//添加realm對(duì)象
securityManager.setRealm(userRealm);
//添加session管理器對(duì)象
securityManager.setSessionManager(sessionManager);
return securityManager;
}總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot整合Spring?Security實(shí)現(xiàn)權(quán)限控制的全過(guò)程
- SpringBoot集成Sa-Token實(shí)現(xiàn)權(quán)限認(rèn)證流程入門(mén)教程
- springboot整合Sa-Token實(shí)現(xiàn)登錄認(rèn)證和權(quán)限校驗(yàn)的詳細(xì)流程
- SpringBoot越權(quán)和數(shù)據(jù)權(quán)限控制的實(shí)現(xiàn)方案(最新整理)
- springboot3整合SpringSecurity實(shí)現(xiàn)登錄校驗(yàn)與權(quán)限認(rèn)證
- SpringBoot?使用?Sa-Token?完成權(quán)限認(rèn)證的操作方法
- SpringBoot整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫(kù)登錄及權(quán)限控制功能
- 基于SpringBoot、SpringSecurity、JWT、RBAC搭建一套可落地的權(quán)限系統(tǒng)
相關(guān)文章
java操作Redis緩存設(shè)置過(guò)期時(shí)間的方法
這篇文章主要介紹了java操作Redis緩存設(shè)置過(guò)期時(shí)間的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Java如何獲取發(fā)送請(qǐng)求的電腦的IP地址
文章介紹了如何通過(guò)HttpServletRequest獲取客戶(hù)端IP地址,特別是當(dāng)客戶(hù)端通過(guò)代理訪(fǎng)問(wèn)時(shí),如何使用x-forwarded-for頭來(lái)獲取真實(shí)的IP地址2024-11-11
mybatis mybatis-plus-generator+clickhouse自動(dòng)生成代碼案例詳解
這篇文章主要介紹了mybatis mybatis-plus-generator+clickhouse自動(dòng)生成代碼案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
Springboot實(shí)現(xiàn)Java阿里短信發(fā)送代碼實(shí)例
這篇文章主要介紹了springboot實(shí)現(xiàn)Java阿里短信發(fā)送代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
SpringController返回值和異常自動(dòng)包裝的問(wèn)題小結(jié)
今天遇到一個(gè)需求,在不改動(dòng)原系統(tǒng)代碼的情況下,將Controller的返回值和異常包裝到一個(gè)統(tǒng)一的返回對(duì)象中去,下面通過(guò)本文給大家介紹SpringController返回值和異常自動(dòng)包裝的問(wèn)題,需要的朋友可以參考下2024-03-03
java仿微信搖一搖實(shí)現(xiàn)播放音樂(lè)
這篇文章主要為大家詳細(xì)介紹了java仿微信搖一搖實(shí)現(xiàn)播放音樂(lè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06

