新聞中心
在講數(shù)據(jù)權(quán)限之前,我們有必要先和大家介紹一下 Spring Security 中的權(quán)限注解,把這個(gè)捋清楚了,再去看 TienChin 項(xiàng)目的權(quán)限注解,你就會(huì)發(fā)現(xiàn)非常容易了。

創(chuàng)新互聯(lián)公司2013年開創(chuàng)至今,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元甘南做網(wǎng)站,已為上家服務(wù),為甘南各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792
1. Spring Security 中的權(quán)限注解
Spring Security 中支持多種權(quán)限注解,首先我們需要通過 @EnableGlobalMethodSecurity 注解開啟權(quán)限注解的使用,方式如下:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
在這個(gè)注解中我們一共設(shè)置了三個(gè)屬性:
- prePostEnabled:這個(gè)表示開啟 Spring Security 提供的四個(gè)權(quán)限注解,@PostAuthorize、@PostFilter、@PreAuthorize 以及 @PreFilter,這四個(gè)注解支持權(quán)限表達(dá)式,支持 SpEL,功能比較豐富。
- securedEnabled:開啟 Spring Security 提供的 @Secured 注解,該注解不支持權(quán)限表達(dá)式。
- jsr250Enabled:開啟 JSR-250 提供的注解,主要包括 @DenyAll、@PermitAll 以及 @RolesAllowed 三個(gè)注解,這些注解也不支持權(quán)限表達(dá)式。
以上這些注解的含義分別如下:
- @PostAuthorize:在目標(biāo)方法執(zhí)行之后進(jìn)行權(quán)限校驗(yàn)。
- @PostFilter:在目標(biāo)方法執(zhí)行之后對(duì)方法的返回結(jié)果進(jìn)行過濾。
- @PreAuthorize:在目標(biāo)方法執(zhí)行之前進(jìn)行權(quán)限校驗(yàn)。
- @PreFilter:在目標(biāo)方法執(zhí)行之前對(duì)方法參數(shù)進(jìn)行過濾。
- @Secured:訪問目標(biāo)方法必須具備相應(yīng)的角色。
- @DenyAll:拒絕所有訪問。
- @PermitAll:允許所有訪問。
- @RolesAllowed:訪問目標(biāo)方法必須具備相應(yīng)的角色。
這是所有的,當(dāng)然一般來說我們只要設(shè)置 prePostEnabled=true 就夠用了,也就是前四個(gè)注解基本上就能滿足大部分需求了。
另外還有一種比較“古老”的方法配置基于方法的權(quán)限管理,那就是通過 XML 文件配置方法攔截規(guī)則,目前已經(jīng)很少有用 XML 文件來配置 Spring Security 了,所以對(duì)于這種方式我們不做過多介紹。感興趣的小伙伴可以查看官網(wǎng)的相關(guān)介紹:https://docs.spring.io/spring-security/ site/docs/5.4.0/reference/html5/#secure-object-impls。
2. 權(quán)限注解實(shí)踐
接下來我們通過幾個(gè)簡(jiǎn)單的案例來學(xué)習(xí)一下上面幾種不同注解的用法。
首先創(chuàng)建一個(gè) Spring Boot 項(xiàng)目,引入 Web 和 Spring Security 依賴,項(xiàng)目創(chuàng)建完成后,添加如下配置文件:
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig{
}
為了方便起見,我們將使用單元測(cè)試進(jìn)行驗(yàn)證,所以這里就不進(jìn)行額外的配置了,通過 @EnableGlobalMethodSecurity 注解開啟其他權(quán)限注解的使用即可。
接下來創(chuàng)建一個(gè) User 類以備后續(xù)使用:
public class User {
private Integer id;
private String username;
//省略getter/setter
}準(zhǔn)備工作完成后,我們來逐個(gè)講解一下前面注解的用法。
2.1 @PreAuthorize
@PreAuthorize 注解可以在目標(biāo)方法執(zhí)行之前對(duì)其進(jìn)行安全校驗(yàn),在安全校驗(yàn)時(shí),可以直接使用權(quán)限表達(dá)式。例如可以定義如下方法:
@Service
public class HelloService {
@PreAuthorize("hasRole('ADMIN')")
public String hello() {
return "hello";
}
}
這里使用了權(quán)限表達(dá)式 hasRole,表示執(zhí)行該方法必須具備 ADMIN 角色才可以訪問,否則不可以訪問。接下來我們?cè)趩卧獪y(cè)試中來測(cè)試該方法:
@SpringBootTest
class BasedOnMethodApplicationTests {
@Autowired
HelloService helloService;
@Test
@WithMockUser(roles = "ADMIN")
void preauthorizeTest01() {
String hello = helloService.hello();
assertNotNull(hello);
assertEquals("hello", hello);
}
}
通過 @WithMockUser(roles = "ADMIN") 注解設(shè)定當(dāng)前執(zhí)行的用戶角色是 ADMIN,然后調(diào)用 helloService 中的方法進(jìn)行測(cè)試即可。如果將用戶角色設(shè)置為其他字符,那單元測(cè)試就不會(huì)通過。
當(dāng)然,這里除了 hasRole 表達(dá)式之外,也可以使用其他權(quán)限表達(dá)式,甚至也可以同時(shí)使用多個(gè)權(quán)限表達(dá)式,如下所示:
@Service
public class HelloService {
@PreAuthorize("hasRole('ADMIN') and authentication.name=='javaboy'")
public String hello() {
return "hello";
}
}
表示訪問者名稱必須是 javaboy,而且還需要同時(shí)具備 ADMIN 角色,才可以訪問該方法。此時(shí)通過如下代碼對(duì)其進(jìn)行測(cè)試:
@SpringBootTest
class BasedOnMethodApplicationTests {
@Autowired
HelloService helloService;
@Test
@WithMockUser(roles = "ADMIN",username = "javaboy")
void preauthorizeTest01() {
String hello = helloService.hello();
assertNotNull(hello);
assertEquals("hello", hello);
}
}
在 @PreAuthorize 注解中,還可以通過 # 引用方法的參數(shù),并對(duì)其進(jìn)行校驗(yàn),例如如下方法表示請(qǐng)求者的用戶名必須等于方法參數(shù) name 的值,方法才可以被執(zhí)行:
@PreAuthorize("authentication.name==#name")
public String hello(String name) {
return "hello:" + name;
}測(cè)試方法如下:
@Test
@WithMockUser(username = "javaboy")
void preauthorizeTest02() {
String hello = helloService.hello("javaboy");
assertNotNull(hello);
assertEquals("hello:javaboy", hello);
}
當(dāng)模擬的用戶名和方法參數(shù)相等時(shí),單元測(cè)試就可以通過。
2.2 @PreFilter
@PreFilter 主要是對(duì)方法的請(qǐng)求參數(shù)進(jìn)行過濾,它里邊包含了一個(gè)內(nèi)置對(duì)象 filterObject 表示要過濾的參數(shù),如果方法只有一個(gè)參數(shù),則內(nèi)置的 filterObject 對(duì)象就代表該參數(shù);如果方法有多個(gè)參數(shù),則需要通過 filterTarget 來指定 filterObject 到底代表哪個(gè)對(duì)象:
@PreFilter(value = "filterObject.id%2!=0",filterTarget = "users")
public void addUsers(Listusers, Integer other) {
System.out.println("users = " + users);
}
上面代碼表示對(duì)方法參數(shù) users 進(jìn)行過濾,將 id 為奇數(shù)的 user 保留。
然后通過單元測(cè)試對(duì)該方法進(jìn)行測(cè)試:
@Test
@WithMockUser(username = "javaboy")
void preFilterTest01() {
Listusers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
users.add(new User(i, "javaboy:" + i));
}
helloService.addUsers(users, 99);
}
執(zhí)行單元測(cè)試方法,addUsers 方法中只會(huì)打印出 id 為奇數(shù)的 user 對(duì)象。
2.3 @PostAuthorize
@PostAuthorize 是在目標(biāo)方法執(zhí)行之后進(jìn)行權(quán)限校驗(yàn)。可能有小伙伴會(huì)覺得奇怪,目標(biāo)方法都執(zhí)行完了才去做權(quán)限校驗(yàn)意義何在?其實(shí)這個(gè)主要是在 ACL 權(quán)限模型中會(huì)用到,目標(biāo)方法執(zhí)行完畢后,通過 @PostAuthorize 注解去校驗(yàn)?zāi)繕?biāo)方法的返回值是否滿足相應(yīng)的權(quán)限要求。
不過呢,即使你的權(quán)限模型不是 ACL,也沒關(guān)系,也有可能用到這個(gè)注解,反正記得它的作用:方法執(zhí)行完成后,根據(jù)用戶的權(quán)限信息過濾出需要返回給用戶的數(shù)據(jù)。
從技術(shù)角度來講,@PostAuthorize 注解中也可以使用權(quán)限表達(dá)式,但是在實(shí)際開發(fā)中權(quán)限表達(dá)式一般都是結(jié)合 @PreAuthorize 注解一起使用的。@PostAuthorize 包含一個(gè)內(nèi)置對(duì)象 returnObject,表示方法的返回值,開發(fā)者可以對(duì)返回值進(jìn)行校驗(yàn):
@PostAuthorize("returnObject.id==1")
public User getUserById(Integer id) {
return new User(id, "javaboy");
}這個(gè)表示方法返回的 user 對(duì)象的 id 必須為 1,調(diào)用才會(huì)順利通過,否則就會(huì)拋出異常。
然后通過單元測(cè)試對(duì)該方法進(jìn)行測(cè)試:
@Test
@WithMockUser(username = "javaboy")
void postAuthorizeTest01() {
User user = helloService.getUserById(1);
assertNotNull(user);
assertEquals(1,user.getId());
assertEquals("javaboy",user.getUsername());
}
如果調(diào)用時(shí)傳入的參數(shù)為 1,單元測(cè)試就會(huì)順利通過。
2.4 @PostFilter
@PostFilter 注解是在目標(biāo)方法執(zhí)行之后,對(duì)目標(biāo)方法的返回結(jié)果進(jìn)行過濾,該注解中包含了一個(gè)內(nèi)置對(duì)象 filterObject,表示目標(biāo)方法返回的集合/數(shù)組中的具體元素:
@PostFilter("filterObject.id%2==0")
public List getAll() {
List users = new ArrayList<>();
for (int i = 0; i < 10; i++) {
users.add(new User(i, "javaboy:" + i));
}
return users;
} 這段代碼表示 getAll 方法的返回值 users 集合中 user 對(duì)象的 id 必須為偶數(shù)。
然后我們通過單元測(cè)試對(duì)其進(jìn)行測(cè)試,代碼如下:
@Test
@WithMockUser(roles = "ADMIN")
void postFilterTest01() {
Listall = helloService.getAll();
assertNotNull(all);
assertEquals(5, all.size());
assertEquals(2,all.get(1).getId());
}
2.5 @Secured
@Secured 注解也是 Spring Security 提供的權(quán)限注解,不同于前面四個(gè)注解,該注解不支持權(quán)限表達(dá)式,只能做一些簡(jiǎn)單的權(quán)限描述。
@Secured({"ROLE_ADMIN","ROLE_USER"})
public User getUserByUsername(String username) {
return new User(99, username);
}這段代碼表示用戶需要具備 ROLE_ADMIN 或者 ROLE_USER 角色,才能訪問 getUserByUsername 方法。
然后我們通過單元測(cè)試對(duì)其進(jìn)行測(cè)試,代碼如下:
@Test
@WithMockUser(roles = "ADMIN")
void securedTest01() {
User user = helloService.getUserByUsername("javaboy");
assertNotNull(user);
assertEquals(99,user.getId());
assertEquals("javaboy", user.getUsername());
}
注意,這里不需要給角色添加 ROLE_ 前綴,系統(tǒng)會(huì)自動(dòng)添加。
2.6 @DenyAll
@DenyAll 是 JSR-250 提供的方法注解,看名字就知道這是拒絕所有訪問:
@DenyAll
public String denyAll() {
return "DenyAll";
}
然后我們通過單元測(cè)試對(duì)其進(jìn)行測(cè)試,代碼如下:
@Test
@WithMockUser(username = "javaboy")
void denyAllTest01() {
helloService.denyAll();
}
在單元測(cè)試過程中,就會(huì)拋出異常。
2.7 @PermitAll
@PermitAll 也是 JSR-250 提供的方法注解,看名字就知道這是允許所有訪問:
@PermitAll
public String permitAll() {
return "PermitAll";
}
然后我們通過單元測(cè)試對(duì)其進(jìn)行測(cè)試,代碼如下:
@Test
@WithMockUser(username = "javaboy")
void permitAllTest01() {
String s = helloService.permitAll();
assertNotNull(s);
assertEquals("PermitAll", s);
}
2.8 @RolesAllowed
@RolesAllowed 也是 JSR-250 提供的注解,可以添加在方法上或者類上,當(dāng)添加在類上時(shí),表示該注解對(duì)類中的所有方法生效;如果類上和方法上都有該注解,并且起沖突,則以方法上的注解為準(zhǔn)。我們來看一個(gè)簡(jiǎn)單的案例:
@RolesAllowed({"ADMIN","USER"})
public String rolesAllowed() {
return "RolesAllowed";
}這個(gè)表示訪問 rolesAllowed 方法需要具備 ADMIN 或者 USER 角色,然后我們通過單元測(cè)試對(duì)其進(jìn)行測(cè)試,代碼如下:
@Test
@WithMockUser(roles = "ADMIN")
void rolesAllowedTest01() {
String s = helloService.rolesAllowed();
assertNotNull(s);
assertEquals("RolesAllowed", s);
}
這就是常見的方法權(quán)限注解。
文章名稱:想要控制好權(quán)限,這八個(gè)注解你必須知道!
本文網(wǎng)址:http://fisionsoft.com.cn/article/dpoggpc.html


咨詢
建站咨詢
