数据库有一个权限表t_permission
,
id | url |
---|---|
1 | /list |
2 | /list/{id} |
3 | /item |
4 | /item/{id} |
5 | /list/test/{id} |
6 | /list/{type}/{id} |
7 | /list/test2/3 |
所有url
都是对应 spring boot 3.5.0
@PostMapping
访问前先进拦截器,查看有没有访问权限,获取请求url
,然后根据url
去查是否有匹配的,现在静态的1
,3
,7
直接查询是否相等就可以了,对于动态2
,4
,5
,6
怎么匹配?
比如访问链接是/list/test/1
,
是不是先查看/list/test/1
有没有,
没有再查/list/test/{id}
,
没有再查/list/{id}/{id}
,
没有再查/{id}/{id}/{id}
...
这样查下去感觉组合好多种呀?具体要怎么做,spring boot 3.5.0
@PostMapping
注解是怎么匹配的?
还是说我这思路错了?,或者数据库url
路径不是这么保存的?
现在我实现的方案就是全部用静态路劲,参数传递都是 @RequestParam
或 @RequestBody
,对于动态地址,不知道怎么搞?
这个只是权限表一个简单说明,实际用户角色关联权限,用户访问根据用户id
和url
查权限表,现在主要是这个URL
查询搞不定。
高手指点一下。
完整代码库:https://gitee.com/kaipizhe/kaipizhe-test
SecurityConfig.java
package com.kaipizhe.config;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.List;
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class SecurityConfig {
@Value("${jwt.public.key}")
RSAPublicKey key;
@Value("${jwt.private.key}")
RSAPrivateKey priv;
private final CustomAuthorizationManager customAuthorizationManager;
public SecurityConfig(CustomAuthorizationManager customAuthorizationManager) {
this.customAuthorizationManager = customAuthorizationManager;
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.httpBasic(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
.logout(AbstractHttpConfigurer::disable)
.cors(Customizer.withDefaults())
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/api/**").access(customAuthorizationManager)
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> {
oauth2.jwt(Customizer.withDefaults());
})
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(new BearerTokenAccessDeniedHandler())
);
return http.build();
}
@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(this.key).build();
}
@Bean
JwtEncoder jwtEncoder() {
JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jwks);
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowedMethods(Arrays.asList("POST", "GET", "DELETE", "PUT", "OPTIONS"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
CustomAuthorizationManager.java
package com.kaipizhe.config;
import com.kaipizhe.service.PermissionService;
import jakarta.validation.constraints.NotNull;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.function.Supplier;
@Component
public class CustomAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
private final PermissionService permissionService;
public CustomAuthorizationManager(PermissionService permissionService) {
this.permissionService = permissionService;
}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
return getAuthorizationDecision(authentication, object, permissionService);
}
@NotNull
static AuthorizationDecision getAuthorizationDecision(Supplier<Authentication> authentication, RequestAuthorizationContext object, PermissionService permissionService) {
if (authentication.get() != null) {
// 创始人直接放行
long count = authentication.get().getAuthorities().stream().filter(o -> o.getAuthority().equals("SCOPE_ROLE_1")).count();
if (count > 0) {
return new AuthorizationDecision(true);
} else {
// 是不是在这个地方可以通过真实的url获取到controller中动态的url,spring本身自己有一个方法,是什么,有那位大哥知道不?
// 再通过动态的url直接去数据库查询就可以了
String url = object.getRequest().getRequestURI();
String username = authentication.get().getName();
if (StringUtils.hasText(url) && StringUtils.hasText(username) && permissionService.getPermissionByUrlUsername(url, username) != null) {
return new AuthorizationDecision(true);
}
}
}
throw new AccessDeniedException("没有权限访问!");
}
}
就是再 CustomAuthorizationManager.java
的 getAuthorizationDecision()
方法中怎么根据真实的URL
去获取controller
中动态的url
?
不建议递归方案
你提到的
/list/test/1
→/list/test/{id}
→/list/{id}/{id}
→/{id}/{id}/{id}
这种方式有几个问题:实际使用建议
对于你的场景,建议:
@RequestParam
/list/{id}
这样的模式/list/**
权限就拥有所有list相关的权限匹配策略
使用HandlerInterceptorAdapter和@RequestMappingHandlerMapping
基于映射机制的逻辑:获取映射模式 → 查询权限表 → 放行/拒绝
CustomAuthorizationManager.java 文件修改
CustomAuthorizationManager.java 改进匹配前缀
创建自定义的处理器映射信息提取器