SpringSecurity怎樣使用注解控制權(quán)限
一般的系統(tǒng)在權(quán)限設(shè)計(jì)上,都會(huì)分為角色、權(quán)限(RDBC),復(fù)雜一點(diǎn)的可能會(huì)有用戶組、組織之類的概念。
用戶的權(quán)限是寫死的,對應(yīng)于后臺(tái)的接口或者資源,是沒辦法改變的,一般不對用戶開放修改權(quán)限。
管理員用戶可以通過給角色分配權(quán)限的方式,來實(shí)現(xiàn)訪問控制。
所以當(dāng)我們寫過濾器,或者用一些安全框架時(shí)(比如Shiro,Spring Security),也需要將可變的“角色”,轉(zhuǎn)化為不可變的“權(quán)限”,注入到框架中。
具體的可以看我之前寫的一篇(Spring Security整合jwt完整版)
注入當(dāng)前用戶的權(quán)限后,就需要進(jìn)行訪問控制了。
常見的做法有
1、路徑比對
之前有個(gè)項(xiàng)目用過一次,定義一個(gè)過濾器,添加到security的過濾鏈中,在這個(gè)過濾器中做這么一件事:
分析當(dāng)前訪問路徑所需要的權(quán)限,檢查當(dāng)前用戶是否具有該權(quán)限,做一個(gè)對比,根據(jù)對比結(jié)果來決定當(dāng)前用戶是否可以訪問該資源。
這種做法的好處是代碼的入侵性不高,不需要再每個(gè)接口上加注解。但相對來說,顯得不那么直觀,可讀性比較差,所以這次換個(gè)方法。
2、使用注解的方式
SpringSecurity使用注解來控制訪問時(shí),需要提前開啟這個(gè)功能。
在配置類上加上注解
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
在接口中如此使用
/**
* 條件查詢
*/
@PreAuthorize("hasAuthority('IMPORT:SELECT')")
@ApiOperation(value = "查詢")
@GetMapping("/list")
public R<Page<Task>> list(TaskDto taskDto){
//測試階段,隨機(jī)生成任務(wù)
Random random = new Random();
//todo
int i = random.nextInt(4);
if(i == 2) {
metroServerAdapterService.reloadShelveTask();
}
Page<Task> list = taskService.list(taskDto);
return R.ok(list);
}
hasAuthority可以替換成hasRole,雖然可以達(dá)到相同的目的,但是在使用的方法上還是有些不同的。
hasRole要求內(nèi)容必須以"ROLE_"開頭,也是官方推薦的命名方式,否則沒有效果。但是hasAuthority沒有限制,數(shù)據(jù)庫中怎樣寫的,代碼里就怎么寫。
同樣的功能的注解為什么要有兩個(gè)名字呢?;蛟S這么做可能在語義上比較清晰明確一點(diǎn),將角色與權(quán)限這兩個(gè)概念稍加區(qū)分。
仔細(xì)想一下,確實(shí)有些小型的系統(tǒng)或許壓根就不需要權(quán)限,只有給用戶分配角色,沒有給角色分配權(quán)限這一過程。這樣的話,角色也是不可變的,就可以根據(jù)角色來做訪問控制了。
但考慮通用性,個(gè)人覺得用hasAuthority就可以了。
SpringSecurity_權(quán)限注解@PreAuthorize、@PostAuthorize
spring是如何實(shí)現(xiàn)對HTTP請求進(jìn)行安全檢查和資源使用授權(quán)的?
實(shí)現(xiàn)過程由類AbstractSecurityInterceptor在beforeInvocation方法中完成,在beforeInvocation的實(shí)現(xiàn)中,
首先,需要讀取IoC容器中Bean的配置,在這些屬性配置中配置了對HTTP請求資源的安全需求,
比如,哪個(gè)角色的用戶可以接入哪些URL請求資源,具體實(shí)現(xiàn)邏輯見:
#與Web環(huán)境的接口FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter
@PreAuthorize、@PostAuthorize注解實(shí)現(xiàn)邏輯
繼承根節(jié)點(diǎn):SecurityMetaSource
可以通過Spring注解聲明,需要依賴類注入,實(shí)現(xiàn)權(quán)限靈活配置如:
@Component("securityMetadataSource")
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource
PrePostAnnotationSecurityMetadataSource類繼承關(guān)系
AbstractMethodSecurityMetadataSource類繼承關(guān)系
package org.springframework.security.access.prepost;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;
import org.springframework.util.ClassUtils;
public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
private final PrePostInvocationAttributeFactory attributeFactory;
public PrePostAnnotationSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory) {
this.attributeFactory = attributeFactory;
}
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return Collections.emptyList();
} else {
this.logger.trace("Looking for Pre/Post annotations for method '" + method.getName() + "' on target class '" + targetClass + "'");
PreFilter preFilter = (PreFilter)this.findAnnotation(method, targetClass, PreFilter.class);
PreAuthorize preAuthorize = (PreAuthorize)this.findAnnotation(method, targetClass, PreAuthorize.class);
PostFilter postFilter = (PostFilter)this.findAnnotation(method, targetClass, PostFilter.class);
PostAuthorize postAuthorize = (PostAuthorize)this.findAnnotation(method, targetClass, PostAuthorize.class);
if (preFilter == null && preAuthorize == null && postFilter == null && postAuthorize == null) {
this.logger.trace("No expression annotations found");
return Collections.emptyList();
} else {
String preFilterAttribute = preFilter == null ? null : preFilter.value();
String filterObject = preFilter == null ? null : preFilter.filterTarget();
String preAuthorizeAttribute = preAuthorize == null ? null : preAuthorize.value();
String postFilterAttribute = postFilter == null ? null : postFilter.value();
String postAuthorizeAttribute = postAuthorize == null ? null : postAuthorize.value();
ArrayList<ConfigAttribute> attrs = new ArrayList(2);
PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(preFilterAttribute, filterObject, preAuthorizeAttribute);
if (pre != null) {
attrs.add(pre);
}
PostInvocationAttribute post = this.attributeFactory.createPostInvocationAttribute(postFilterAttribute, postAuthorizeAttribute);
if (post != null) {
attrs.add(post);
}
attrs.trimToSize();
return attrs;
}
}
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
private <A extends Annotation> A findAnnotation(Method method, Class<?> targetClass, Class<A> annotationClass) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
A annotation = AnnotationUtils.findAnnotation(specificMethod, annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on specific method: " + specificMethod);
return annotation;
} else {
if (specificMethod != method) {
annotation = AnnotationUtils.findAnnotation(method, annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on: " + method);
return annotation;
}
}
annotation = AnnotationUtils.findAnnotation(specificMethod.getDeclaringClass(), annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on: " + specificMethod.getDeclaringClass().getName());
return annotation;
} else {
return null;
}
}
}
}
//注解開啟權(quán)限 @EnableResourceServer @EnableGlobalMethodSecurity SecurityContextHolder作為全局緩存,從上下文獲取授權(quán)信息 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
上面權(quán)限列表初始化由具體實(shí)現(xiàn)類實(shí)現(xiàn):
public class User implements UserDetails, CredentialsContainer {
...
private final Set<GrantedAuthority> authorities;
...
//authorities權(quán)限列表
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 詳解Spring Security中權(quán)限注解的使用
- 詳解如何在Spring?Security中自定義權(quán)限表達(dá)式
- SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)動(dòng)態(tài)權(quán)限問題
- Spring?Security權(quán)限管理實(shí)現(xiàn)接口動(dòng)態(tài)權(quán)限控制
- Spring Security動(dòng)態(tài)權(quán)限的實(shí)現(xiàn)方法詳解
- Spring?Security權(quán)限注解啟動(dòng)及邏輯處理使用示例
相關(guān)文章
Spring依賴注入的兩種方式(根據(jù)實(shí)例詳解)
這篇文章主要介紹了Spring依賴注入的兩種方式(根據(jù)實(shí)例詳解),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05
JAVA下單接口優(yōu)化實(shí)戰(zhàn)TPS性能提高10倍
今天小編就為大家分享一篇關(guān)于JAVA下單接口優(yōu)化實(shí)戰(zhàn)TPS性能提高10倍,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12

