SpringSecurity實(shí)現(xiàn)動(dòng)態(tài)權(quán)限校驗(yàn)的過程
在框架DefaultSecurityFilterChain源碼內(nèi)打斷點(diǎn)可以找到SpringSecurity的過濾器鏈可以看見一個(gè)叫AuthorizationFilter的過濾器

很明顯這個(gè)叫authorizationManager的應(yīng)該是我們要找的玩意,直接去AuthorizationFilter內(nèi)找這個(gè)類看他的源碼可以發(fā)現(xiàn)check方法已經(jīng)棄用,他推薦用的方法是authorize但這玩意也還是調(diào)用的check
@FunctionalInterface
public interface AuthorizationManager<T> {
/**
* Determines if access should be granted for a specific authentication and object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @throws AccessDeniedException if access is not granted
*/
default void verify(Supplier<Authentication> authentication, T object) {
AuthorizationDecision decision = check(authentication, object);
if (decision != null && !decision.isGranted()) {
throw new AuthorizationDeniedException("Access Denied", decision);
}
}
/**
* Determines if access is granted for a specific authentication and object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @return an {@link AuthorizationDecision} or null if no decision could be made
* @deprecated please use {@link #authorize(Supplier, Object)} instead
*/
@Nullable
@Deprecated
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
/**
* Determines if access is granted for a specific authentication and object.
* @param authentication the {@link Supplier} of the {@link Authentication} to
* authorize
* @param object the {@link T} object to authorize
* @return an {@link AuthorizationResult}
* @since 6.4
*/
@Nullable
default AuthorizationResult authorize(Supplier<Authentication> authentication, T object) {
return check(authentication, object);
}繼續(xù)往下面看可以看見他是進(jìn)行了校驗(yàn)然后返回了一個(gè)布爾值
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
boolean granted = this.authorizationStrategy.isGranted(authentication.get());
return new AuthorizationDecision(granted);
}代碼實(shí)現(xiàn) 邏輯大概是通過傳進(jìn)來的接口路徑然后匹配權(quán)限
@Component
public class DynamicAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
@Resource
private DynamicSecurityMetadataSource securityMetadataSource;
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
HttpServletRequest request = context.getRequest();
// 獲取當(dāng)前請求所需的權(quán)限
String url = request.getRequestURI();
String method = request.getMethod();
FilterInvocation fi = new FilterInvocation(String.valueOf(request), url, method);
Collection<ConfigAttribute> attributes = securityMetadataSource.getAttributes(fi);
// 沒有配置權(quán)限要求,允許訪問
if (CollectionUtils.isEmpty(attributes)) {
return new AuthorizationDecision(true);
}
// 獲取當(dāng)前用戶認(rèn)證信息
Authentication auth = authentication.get();
if (auth == null || !auth.isAuthenticated()) {
return new AuthorizationDecision(false);
}
// 獲取用戶所擁有的權(quán)限
Set<String> userPermissions = auth.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
// 判斷是否有所需權(quán)限
boolean hasPermission = attributes.stream()
.map(ConfigAttribute::getAttribute)
.anyMatch(userPermissions::contains);
return new AuthorizationDecision(hasPermission);
}
}getAllConfigAttributes和supports我大概看了一下直接復(fù)制粘貼的框架的源碼以后萬一有用呢
@Component
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Resource
private TPMenuService menuService;
private Map<String, Collection<ConfigAttribute>> configAttributeMap;
@PostConstruct
public void loadDataSource() {
configAttributeMap = new HashMap<>();
List<TPMenu> menus = menuService.list();
configAttributeMap = menus.stream()
.filter(menu -> StringUtils.hasText(menu.getPath()) && StringUtils.hasText(menu.getPerms()))
.collect(Collectors.toMap(
TPMenu::getPath,
menu -> {
List<ConfigAttribute> attributes = new ArrayList<>();
attributes.add(new SecurityConfig(menu.getPerms()));
return attributes;
}
));
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
String requestUrl = ((FilterInvocation) object).getRequestUrl();
if (requestUrl.contains("?")) {
requestUrl = requestUrl.substring(0, requestUrl.indexOf("?"));
}
int count = StringUtils.countOccurrencesOf(requestUrl, "/");
if (count > 2) {
requestUrl = requestUrl.replaceAll("/[^/]+$", "");
}
for (Map.Entry<String, Collection<ConfigAttribute>> entry : configAttributeMap.entrySet()) {
String pattern = entry.getKey();
if (new AntPathMatcher().match(pattern, requestUrl)) {
return entry.getValue();
}
}
return null;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
Set<ConfigAttribute> allAttributes = new HashSet<>();
configAttributeMap.values().forEach(allAttributes::addAll);
return allAttributes;
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}過濾器實(shí)現(xiàn)然后記得security配置文件添加這個(gè)過濾器就ok了,配置文件可以看我另外一篇文章
@Component
public class DynamicSecurityFilter extends OncePerRequestFilter {
@Resource
private DynamicSecurityMetadataSource securityMetadataSource;
@Resource
private DynamicAuthorizationManager authorizationManager;
@Resource
private AccessDeniedHandler accessDeniedHandler;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException, IOException {
if (shouldNotFilter(request)) {
chain.doFilter(request, response);
return;
}
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
RequestAuthorizationContext context = new RequestAuthorizationContext(request);
// 權(quán)限檢查
AuthorizationDecision check = authorizationManager.check(
() -> authentication,
context
);
if (check.isGranted()) {
chain.doFilter(request, response);
} else {
accessDeniedHandler.handle(request, response, new AccessDeniedException("權(quán)限不足"));
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ServletException e) {
throw new RuntimeException(e);
}
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
return whitelist.stream()
.anyMatch(pattern ->
pattern.endsWith("/**")
? path.startsWith(pattern.substring(0, pattern.length() - 3))
: pattern.equals(path)
);
}
}debug重啟可以看見我的過濾器已經(jīng)添加進(jìn)去了

如果有需要還可以直接去看官方demo
https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/java/jwt/login/src/main
到此這篇關(guān)于SpringSecurity實(shí)現(xiàn)動(dòng)態(tài)權(quán)限校驗(yàn)的過程的文章就介紹到這了,更多相關(guān)SpringSecurity動(dòng)態(tài)權(quán)限校驗(yàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA中Directory創(chuàng)建多級目錄的實(shí)現(xiàn)
本文主要介紹了IDEA中Directory創(chuàng)建多級目錄的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
Java中Map實(shí)現(xiàn)線程安全的3種方式
本文主要介紹了Java中Map實(shí)現(xiàn)線程安全的3種方式,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
Spring MVC下 bootStrap服務(wù)器分頁代碼
因?yàn)镾pring 對于ajax直接返回對象,到了WEB頁面就轉(zhuǎn)換成json 所以不需要使用JSON轉(zhuǎn)換封裝可以直接使用。接下來通過本文給大家分享Spring MVC下 bootStrap服務(wù)器分頁代碼,需要的的朋友參考下2017-03-03
Java中finally關(guān)鍵字對返回值的影響詳解
這篇文章主要介紹了Java中finally關(guān)鍵字對返回值的影響詳解,執(zhí)行完try catch里面內(nèi)容準(zhǔn)備return時(shí),如果還有finally需要執(zhí)行這是編譯器會(huì)為我們增加一個(gè)全局變量去暫存return 的值,等到finally執(zhí)行完成去return這個(gè)全局變量,需要的朋友可以參考下2024-01-01
Java基于分治法實(shí)現(xiàn)的快速排序算法示例
這篇文章主要介紹了Java基于分治法實(shí)現(xiàn)的快速排序算法,結(jié)合實(shí)例形式分析了java基于分治法的快速排序相關(guān)實(shí)現(xiàn)技巧,代碼中備有較為詳細(xì)的注釋說明便于理解,需要的朋友可以參考下2017-12-12
淺談SpringBoot Bean加載優(yōu)先級的問題
這篇文章主要介紹了淺談SpringBoot Bean加載優(yōu)先級的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
mybatis于xml方式和注解方式實(shí)現(xiàn)多表查詢的操作方法
在數(shù)據(jù)庫中,單表的操作是最簡單的,但是在實(shí)際業(yè)務(wù)中最少也有十幾張表,并且表與表之間常常相互間聯(lián)系,本文給大家介紹mybatis于xml方式和注解方式實(shí)現(xiàn)多表查詢的操作方法,感興趣的朋友一起看看吧2023-12-12
JAVA序列化和反序列化的底層實(shí)現(xiàn)原理解析
這篇文章主要介紹了JAVA序列化和反序列化的底層實(shí)現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11

