亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Security動態(tài)權(quán)限的實現(xiàn)方法詳解

 更新時間:2022年06月16日 11:15:36   作者:江南一點雨  
這篇文章主要和小伙伴們簡單介紹下 Spring Security 中的動態(tài)權(quán)限方案,以便于小伙伴們更好的理解 TienChin 項目中的權(quán)限方案,感興趣的可以了解一下

最近在做 TienChin 項目,用的是 RuoYi-Vue 腳手架,在這個腳手架中,訪問某個接口需要什么權(quán)限,這個是在代碼中硬編碼的,具體怎么實現(xiàn)的,松哥下篇文章來和大家分析,有的小伙伴可能希望能讓這個東西像 vhr 一樣,可以在數(shù)據(jù)庫中動態(tài)配置,因此這篇文章和小伙伴們簡單介紹下 Spring Security 中的動態(tài)權(quán)限方案,以便于小伙伴們更好的理解 TienChin 項目中的權(quán)限方案。

1. 動態(tài)管理權(quán)限規(guī)則

通過代碼來配置 URL 攔截規(guī)則和請求 URL 所需要的權(quán)限,這樣就比較死板,如果想要調(diào)整訪問某一個 URL 所需要的權(quán)限,就需要修改代碼。

動態(tài)管理權(quán)限規(guī)則就是我們將 URL 攔截規(guī)則和訪問 URL 所需要的權(quán)限都保存在數(shù)據(jù)庫中,這樣,在不改變源代碼的情況下,只需要修改數(shù)據(jù)庫中的數(shù)據(jù),就可以對權(quán)限進(jìn)行調(diào)整。

1.1 數(shù)據(jù)庫設(shè)計

簡單起見,我們這里就不引入權(quán)限表了,直接使用角色表,用戶和角色關(guān)聯(lián),角色和資源關(guān)聯(lián),設(shè)計出來的表結(jié)構(gòu)如圖 13-9 所示。

圖13-9  一個簡單的權(quán)限數(shù)據(jù)庫結(jié)構(gòu)

menu 表是相當(dāng)于我們的資源表,它里邊保存了訪問規(guī)則,如圖 13-10 所示。

圖13-10  訪問規(guī)則

role 是角色表,里邊定義了系統(tǒng)中的角色,如圖 13-11 所示。

圖13-11  用戶角色表

user 是用戶表,如圖 13-12 所示。

圖13-12  用戶表

user_role 是用戶角色關(guān)聯(lián)表,用戶具有哪些角色,可以通過該表體現(xiàn)出來,如圖 13-13 所示。

圖13-13  用戶角色關(guān)聯(lián)表

menu_role 是資源角色關(guān)聯(lián)表,訪問某一個資源,需要哪些角色,可以通過該表體現(xiàn)出來,如圖 13-14 所示。

圖13-14  資源角色關(guān)聯(lián)表

至此,一個簡易的權(quán)限數(shù)據(jù)庫就設(shè)計好了(在本書提供的案例中,有SQL腳本)。

1.2 實戰(zhàn)

項目創(chuàng)建

創(chuàng)建 Spring Boot 項目,由于涉及數(shù)據(jù)庫操作,這里選用目前大家使用較多的 MyBatis 框架,所以除了引入 Web、Spring Security 依賴之外,還需要引入 MyBatis 以及 MySQL 依賴。

最終的 pom.xml 文件內(nèi)容如下:

<dependencies>
????<dependency>
????????<groupId>org.springframework.boot</groupId>
????????<artifactId>spring-boot-starter-security</artifactId>
????</dependency>
????<dependency>
????????<groupId>org.springframework.boot</groupId>
????????<artifactId>spring-boot-starter-web</artifactId>
????</dependency>
????<dependency>
????????<groupId>org.mybatis.spring.boot</groupId>
????????<artifactId>mybatis-spring-boot-starter</artifactId>
????????<version>2.1.3</version>
????</dependency>
????<dependency>
????????<groupId>mysql</groupId>
????????<artifactId>mysql-connector-java</artifactId>
????????<scope>runtime</scope>
????</dependency>
</dependencies>

項目創(chuàng)建完成后,接下來在 application.properties 中配置數(shù)據(jù)庫連接信息:

spring.datasource.username=root
spring.datasource.password=123
spring.datasource.url=jdbc:mysql:///security13?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

配置完成后,我們的準(zhǔn)備工作就算完成了。

創(chuàng)建實體類

根據(jù)前面設(shè)計的數(shù)據(jù)庫,我們需要創(chuàng)建三個實體類。

首先來創(chuàng)建角色類 Role:

public?class?Role?{
????private?Integer?id;
????private?String?name;
????private?String?nameZh;
???????//省略getter/setter
}

然后創(chuàng)建菜單類 Menu:

public?class?Menu?{
????private?Integer?id;
????private?String?pattern;
????private?List<Role>?roles;
????//省略getter/setter
}

菜單類中包含一個 roles 屬性,表示訪問該項資源所需要的角色。

最后我們創(chuàng)建 User 類:

public?class?User?implements?UserDetails?{
????private?Integer?id;
????private?String?password;
????private?String?username;
????private?boolean?enabled;
????private?boolean?locked;
????private?List<Role>?roles;
????@Override
????public?Collection<??extends?GrantedAuthority>?getAuthorities()?{
????????return?roles.stream()
????????????????????????.map(r?->?new?SimpleGrantedAuthority(r.getName()))
????????????????????????.collect(Collectors.toList());
????}
????@Override
????public?String?getPassword()?{
????????return?password;
????}
????@Override
????public?String?getUsername()?{
????????return?username;
????}
????@Override
????public?boolean?isAccountNonExpired()?{
????????return?true;
????}
????@Override
????public?boolean?isAccountNonLocked()?{
????????return?!locked;
????}
????@Override
????public?boolean?isCredentialsNonExpired()?{
????????return?true;
????}
????@Override
????public?boolean?isEnabled()?{
????????return?enabled;
????}
????//省略其他getter/setter
}

由于數(shù)據(jù)庫中有 enabled 和 locked 字段,所以 isEnabled() 和 isAccountNonLocked() 兩個方法如實返回,其他幾個賬戶狀態(tài)方法默認(rèn)返回 true 即可。在 getAuthorities() 方法中,我們對 roles 屬性進(jìn)行遍歷,組裝出新的集合對象返回即可。

創(chuàng)建Service

接下來我們創(chuàng)建 UserService 和 MenuService,并提供相應(yīng)的查詢方法。

先來看 UserService:

@Service
public?class?UserService?implements?UserDetailsService?{
????@Autowired
????UserMapper?userMapper;
????@Override
????public?UserDetails?loadUserByUsername(String?username)?
?????????????????????????????????????????????throws?UsernameNotFoundException?{
????????User?user?=?userMapper.loadUserByUsername(username);
????????if?(user?==?null)?{
????????????throw?new?UsernameNotFoundException("用戶不存在");
????????}
????????user.setRoles(userMapper.getUserRoleByUid(user.getId()));
????????return?user;
????}
}

這段代碼應(yīng)該不用多說了,不熟悉的讀者可以參考本書 2.4 節(jié)。

對應(yīng)的 UserMapper 如下:

@Mapper
public?interface?UserMapper?{
????List<Role>?getUserRoleByUid(Integer?uid);
????User?loadUserByUsername(String?username);
}

UserMapper.xml:

<!DOCTYPE?mapper
????????PUBLIC?"-//mybatis.org//DTD?Mapper?3.0//EN"
????????"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper?namespace="org.javaboy.base_on_url_dy.mapper.UserMapper">
????<select?id="loadUserByUsername"?
?????????????????????????resultType="org.javaboy.base_on_url_dy.model.User">
????????select?*?from?user?where?username=#{username};
????</select>
????<select?id="getUserRoleByUid"?
?????????????????????????resultType="org.javaboy.base_on_url_dy.model.Role">
????????select?r.*?from?role?r,user_role?ur?where?ur.uid=#{uid}?and?ur.rid=r.id
????</select>
</mapper>

再來看 MenuService,該類只需要提供一個方法,就是查詢出所有的 Menu 數(shù)據(jù),代碼如下:

@Service
public?class?MenuService?{
????@Autowired
????MenuMapper?menuMapper;
????public?List<Menu>?getAllMenu()?{
????????return?menuMapper.getAllMenu();
????}
}

MenuMapper:

@Mapper
public?interface?MenuMapper?{
????List<Menu>?getAllMenu();
}

MenuMapper.xml:

<!DOCTYPE?mapper
????????PUBLIC?"-//mybatis.org//DTD?Mapper?3.0//EN"
????????"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper?namespace="org.javaboy.base_on_url_dy.mapper.MenuMapper">
????<resultMap?id="MenuResultMap"?
????????????????????????????????type="org.javaboy.base_on_url_dy.model.Menu">
????????<id?property="id"?column="id"/>
????????<result?property="pattern"?column="pattern"></result>
????????<collection?property="roles"?
??????????????????????????????ofType="org.javaboy.base_on_url_dy.model.Role">
????????????<id?column="rid"?property="id"/>
????????????<result?column="rname"?property="name"/>
????????????<result?column="rnameZh"?property="nameZh"/>
????????</collection>
????</resultMap>
????<select?id="getAllMenu"?resultMap="MenuResultMap">
????????select?m.*,r.id?as?rid,r.name?as?rname,r.nameZh?as?rnameZh?from?menu?m?left?join?menu_role?mr?on?m.`id`=mr.`mid`?left?join?role?r?on?r.`id`=mr.`rid`
????</select>
</mapper>

需要注意,由于每一個 Menu 對象都包含了一個 Role 集合,所以這個查詢是一對多,這里通過 resultMap 來進(jìn)行查詢結(jié)果映射。

至此,所有基礎(chǔ)工作都完成了,接下來配置 Spring Security。

配置Spring Security

回顧 13.3.6 小節(jié)的內(nèi)容,SecurityMetadataSource 接口負(fù)責(zé)提供受保護(hù)對象所需要的權(quán)限。在本案例中,受保護(hù)對象所需要的權(quán)限保存在數(shù)據(jù)庫中,所以我們可以通過自定義類繼承自 FilterInvocationSecurityMetadataSource,并重寫 getAttributes 方法來提供受保護(hù)對象所需要的權(quán)限,代碼如下:

@Component
public?class?CustomSecurityMetadataSource?
?????????????????????????implements?FilterInvocationSecurityMetadataSource?{
????@Autowired
????MenuService?menuService;
????AntPathMatcher?antPathMatcher?=?new?AntPathMatcher();

????@Override
????public?Collection<ConfigAttribute>?getAttributes(Object?object)?
???????????????????????????????????????????????throws?IllegalArgumentException?{
????????String?requestURI?=?
???????????????????((FilterInvocation)?object).getRequest().getRequestURI();
????????List<Menu>?allMenu?=?menuService.getAllMenu();
????????for?(Menu?menu?:?allMenu)?{
????????????if?(antPathMatcher.match(menu.getPattern(),?requestURI))?{
????????????????String[]?roles?=?menu.getRoles().stream()
???????????????????????????????.map(r?->?r.getName()).toArray(String[]::new);
????????????????return?SecurityConfig.createList(roles);
????????????}
????????}
????????return?null;
????}

????@Override
????public?Collection<ConfigAttribute>?getAllConfigAttributes()?{
????????return?null;
????}

????@Override
????public?boolean?supports(Class<?>?clazz)?{
????????return?FilterInvocation.class.isAssignableFrom(clazz);
????}
}

自定義 CustomSecurityMetadataSource 類并實現(xiàn) FilterInvocationSecurityMetadataSource 接口,然后重寫它里邊的三個方法:

  • getAttributes:該方法的參數(shù)是受保護(hù)對象,在基于 URL 地址的權(quán)限控制中,受保護(hù)對象就是 FilterInvocation;該方法的返回值則是訪問受保護(hù)對象所需要的權(quán)限。在該方法里邊,我們首先從受保護(hù)對象 FilterInvocation 中提取出當(dāng)前請求的 URL 地址,例如 /admin/hello,然后通過 menuService 對象查詢出所有的菜單數(shù)據(jù)(每條數(shù)據(jù)中都包含訪問該條記錄所需要的權(quán)限),遍歷查詢出來的菜單數(shù)據(jù),如果當(dāng)前請求的 URL 地址和菜單中某一條記錄的 pattern 屬性匹配上了(例如 /admin/hello 匹配上 /admin/**),那么我們就可以獲取當(dāng)前請求所需要的權(quán)限。從 menu 對象中獲取 roles 屬性,并將其轉(zhuǎn)為一個數(shù)組,然后通過 SecurityConfig.createList 方法創(chuàng)建一個 Collection<ConfigAttribute> 對象并返回。如果當(dāng)前請求的 URL 地址和數(shù)據(jù)庫中 menu 表的所有項都匹配不上,那么最終返回 null。如果返回 null,那么受保護(hù)對象到底能不能訪問呢?這就要看 AbstractSecurityInterceptor 對象中的 rejectPublicInvocations 屬性了,該屬性默認(rèn)為 false,表示當(dāng) getAttributes 方法返回 null 時,允許訪問受保護(hù)對象(回顧 13.4.4 小節(jié)中關(guān)于 AbstractSecurityInterceptor#beforeInvocation 的講解)。
  • getAllConfigAttributes:該方法可以用來返回所有的權(quán)限屬性,以便在項目啟動階段做校驗,如果不需要校驗,則直接返回 null 即可。
  • supports:該方法表示當(dāng)前對象支持處理的受保護(hù)對象是 FilterInvocation。

CustomSecurityMetadataSource 類配置完成后,接下來我們要用它來代替默認(rèn)的 SecurityMetadataSource 對象,具體配置如下:

@Configuration
public?class?SecurityConfig?extends?WebSecurityConfigurerAdapter?{
????@Autowired
????CustomSecurityMetadataSource?customSecurityMetadataSource;
????@Autowired
????UserService?userService;

????@Override
????protected?void?configure(AuthenticationManagerBuilder?auth)?
????????????????????????????????????????????????????????????????throws?Exception?{
????????auth.userDetailsService(userService);
????}

????@Override
????protected?void?configure(HttpSecurity?http)?throws?Exception?{
????????ApplicationContext?applicationContext?=?
??????????????????????????????http.getSharedObject(ApplicationContext.class);
????????http.apply(new?UrlAuthorizationConfigurer<>(applicationContext))
????????????????.withObjectPostProcessor(new?
???????????????????????????ObjectPostProcessor<FilterSecurityInterceptor>()?{
????????????????????@Override
????????????????????public?<O?extends?FilterSecurityInterceptor>?O?
????????????????????????????????????????????????????????????postProcess(O?object)?{
???????????object.setSecurityMetadataSource(customSecurityMetadataSource);
????????????????????????return?object;
????????????????????}
????????????????});
????????http.formLogin()
????????????????.and()
????????????????.csrf().disable();
????}
}

關(guān)于用戶的配置無需多說,我們重點來看 configure(HttpSecurity) 方法。

由于訪問路徑規(guī)則和所需要的權(quán)限之間的映射關(guān)系已經(jīng)保存在數(shù)據(jù)庫中,所以我們就沒有必要在 Java 代碼中配置映射關(guān)系了,同時這里的權(quán)限對比也不會用到權(quán)限表達(dá)式,所以我們通過 UrlAuthorizationConfigurer 來進(jìn)行配置。

在配置的過程中,通過 withObjectPostProcessor 方法調(diào)用 ObjectPostProcessor 對象后置處理器,在對象后置處理器中,將 FilterSecurityInterceptor 中的 SecurityMetadataSource 對象替換為我們自定義的 customSecurityMetadataSource 對象即可。

2. 測試

接下來創(chuàng)建 HelloController,代碼如下:

@RestController
public?class?HelloController?{
????@GetMapping("/admin/hello")
????public?String?admin()?{
????????return?"hello?admin";
????}
????@GetMapping("/user/hello")
????public?String?user()?{
????????return?"hello?user";
????}
????@GetMapping("/guest/hello")
????public?String?guest()?{
????????return?"hello?guest";
????}
????@GetMapping("/hello")
????public?String?hello()?{
????????return?"hello";
????}
}

最后啟動項目進(jìn)行測試。

首先使用 admin/123 進(jìn)行登錄,該用戶具備 ROLE_ADMIN 角色,ROLE_ADMIN 可以訪問 /admin/hello、/user/hello 以及 /guest/hello 三個接口。

接下來使用 user/123 進(jìn)行登錄,該用戶具備 ROLE_USER 角色,ROLE_USER 可以訪問 /user/hello 以及 /guest/hello 兩個接口。

最后使用 javaboy/123 進(jìn)行登錄,該用戶具備 ROLE_GUEST 角色,ROLE_GUEST 可以訪問 /guest/hello 接口。

由于 /hello 接口不包含在 URL-權(quán)限 映射關(guān)系中,所以任何用戶都可以訪問 /hello 接口,包括匿名用戶。如果希望所有的 URL 地址都必須在數(shù)據(jù)庫中配置 URL-權(quán)限 映射關(guān)系后才能訪問,那么可以通過如下配置實現(xiàn):

http.apply(new?UrlAuthorizationConfigurer<>(applicationContext))
????????.withObjectPostProcessor(new??
???????????????????????????ObjectPostProcessor<FilterSecurityInterceptor>()?{
????????????@Override
????????????public?<O?extends?FilterSecurityInterceptor>?O?
???????????????????????????????????????????????????????????postProcess(O?object)?{???
???????????object.setSecurityMetadataSource(customSecurityMetadataSource);
????????????????object.setRejectPublicInvocations(true);
????????????????return?object;
????????????}
????????});

通過設(shè)置 FilterSecurityInterceptor 中的 rejectPublicInvocations 屬性為 true,就可以關(guān)閉URL的公開訪問,所有 URL 必須具備對應(yīng)的權(quán)限才能訪問。

以上就是Spring Security動態(tài)權(quán)限的實現(xiàn)方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring Security動態(tài)權(quán)限的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 關(guān)于MyBatis模糊查詢的幾種實現(xiàn)方式

    關(guān)于MyBatis模糊查詢的幾種實現(xiàn)方式

    在實際項目中,我們會經(jīng)常對數(shù)據(jù)做一些模糊查詢的操作,這時候就需要利用到 like字段,那么在Mybatis中,有哪些方式可以實現(xiàn)模糊查詢呢,需要的朋友可以參考下
    2023-05-05
  • Java中的Gradle與Groovy的區(qū)別及存在的關(guān)系

    Java中的Gradle與Groovy的區(qū)別及存在的關(guān)系

    這篇文章主要介紹了Java中的Gradle與Groovy的區(qū)別及存在的關(guān)系,Groovy是一種JVM語言,它可以編譯為與Java相同的字節(jié)碼,并且可以與Java類無縫地互操作,Gradle是Java項目中主要的構(gòu)建系統(tǒng)之一,下文關(guān)于兩者的詳細(xì)內(nèi)容,需要的小伙伴可以參考一下
    2022-02-02
  • JDBC核心技術(shù)詳解

    JDBC核心技術(shù)詳解

    這篇文章主要介紹了JDBC核心技術(shù)詳解,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)JDBC的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • java實現(xiàn)題目以及選項亂序的方法實例

    java實現(xiàn)題目以及選項亂序的方法實例

    這篇文章主要給大家介紹了關(guān)于java實現(xiàn)題目以及選項亂序的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java 獲取上一個月的月份的正確寫法

    Java 獲取上一個月的月份的正確寫法

    這篇文章主要介紹了java獲取上一個月月份,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • 詳解 Corba開發(fā)之Java實現(xiàn)Service與Client

    詳解 Corba開發(fā)之Java實現(xiàn)Service與Client

    這篇文章主要介紹了詳解 Corba開發(fā)之Java實現(xiàn)Service與Client的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-10-10
  • Java中的布隆過濾器原理實現(xiàn)和應(yīng)用

    Java中的布隆過濾器原理實現(xiàn)和應(yīng)用

    Java中的布隆過濾器是一種基于哈希函數(shù)的數(shù)據(jù)結(jié)構(gòu),能夠高效地判斷元素是否存在于一個集合中。它廣泛應(yīng)用于緩存、網(wǎng)絡(luò)協(xié)議、數(shù)據(jù)查詢等領(lǐng)域,在提高程序性能和減少資源消耗方面具有顯著優(yōu)勢
    2023-04-04
  • SpringBoot全局異常與數(shù)據(jù)校驗的方法

    SpringBoot全局異常與數(shù)據(jù)校驗的方法

    這篇文章主要介紹了SpringBoot全局異常與數(shù)據(jù)校驗的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • SpringBoot+Redis實現(xiàn)數(shù)據(jù)字典的方法

    SpringBoot+Redis實現(xiàn)數(shù)據(jù)字典的方法

    這篇文章主要介紹了SpringBoot+Redis實現(xiàn)數(shù)據(jù)字典的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • springboot?aop配合反射統(tǒng)一簽名驗證實踐

    springboot?aop配合反射統(tǒng)一簽名驗證實踐

    這篇文章主要介紹了springboot?aop配合反射統(tǒng)一簽名驗證實踐,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評論