spring 安全 hasAuthority("SCOPE_xxx") 方法不适用于 spring 授权服务器版本 0.2.0
spring security hasAuthority("SCOPE_xxx") method not working with spring authorization server version 0.2.0
我已经使用新的 spring 授权服务器模块创建了一个授权服务器。我能够成功获取令牌,但是当我尝试将令牌用于 hasAuthority()
的受保护端点时,我收到禁止 403 错误。在我的 pom.xml
文件下面
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.erycoking</groupId>
<artifactId>auth-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>auth-service</name>
<description>Auth Service</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2021.0.0</spring-cloud.version>
<jhipster-dependencies.version>7.0.1</jhipster-dependencies.version>
<liquibase.version>4.6.1</liquibase.version>
</properties>
<dependencies>
<dependency>
<groupId>tech.jhipster</groupId>
<artifactId>jhipster-framework</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<!-- Jackson Configurations -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hppc</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>problem-spring-web</artifactId>
<version>0.26.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<!-- Inherited version from Spring Boot can't be used because of regressions -->
<version>${liquibase.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>${liquibase.version}</version>
</plugin>
</plugins>
</build>
</project>
下面是我的授权服务器配置
@Configuration(proxyBeanMethods = false)
public class AuthServerConfig {
private final DataSource dataSource;
private final AuthProperties authProps;
private final TokenSettings tokenSettings;
public AuthServerConfig(DataSource dataSource, AuthProperties authProps, TokenSettings tokenSettings) {
this.dataSource = dataSource;
this.authProps = authProps;
this.tokenSettings = tokenSettings;
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).build();
}
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
JdbcRegisteredClientRepository clientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
clientRepository.save(webClient());
return clientRepository;
}
private RegisteredClient webClient() {
return RegisteredClient.withId("98a9104c-a9c7-4d7c-ad03-ec61bcfeab36")
.clientId(authProps.getClientId())
.clientName(authProps.getClientName())
.clientSecret(authProps.getClientSecret())
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8080/authorized")
.scope("create").scope("read").scope("write").scope("update").scope("delete")
.tokenSettings(tokenSettings)
.build();
}
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
private static RSAKey generateRsa() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder()
.issuer(authProps.getIssuerUri())
.build();
}
}
这是我的安全配置
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig {
private final UserDetailsService userDetailsService;
public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/management/**").permitAll()
.antMatchers("/h2-console/**").permitAll()
//this does not work
.antMatchers(HttpMethod.POST, "/auth/user").hasAuthority(AuthoritiesConstants.ADMIN)
//this does not work
.antMatchers(HttpMethod.GET, "/auth/user").hasAuthority("SCOPE_read")
.anyRequest().authenticated()
.and()
.csrf().disable()
.headers().frameOptions().disable()
.and()
.formLogin(withDefaults())
.userDetailsService(userDetailsService);
return http.build();
}
@Bean
public PasswordEncoder delegatingPasswordEncoder() {
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("bcrypt", encoders);
passwordEncoder.setDefaultPasswordEncoderForMatches(new BCryptPasswordEncoder());
return passwordEncoder;
}
@Bean
public TokenSettings tokenSettings() {
return TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(1))
.refreshTokenTimeToLive(Duration.ofHours(24))
.build();
}
}
这是我的用户详情服务
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
return userRepository.findOneWithRolesByEmailIgnoreCase(login)
.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
if (!user.isActivated()) {
throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
user.getRoles().forEach(e -> {
grantedAuthorities.add(new SimpleGrantedAuthority(e.getName()));
e.getPermissions().forEach(p -> grantedAuthorities.add(new SimpleGrantedAuthority(p.getName())));
});
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), grantedAuthorities);
}
}
当使用令牌向需要单独身份验证的端点发出请求时,它会成功,但是当我尝试在需要角色的端点上使用它时,它会失败。
可能是什么问题?
根据评论中为阐明您的设置而进行的简短讨论,您似乎将授权服务器配置为常规安全应用程序,目的是将范围用作权限。但是,这是对授权服务器的错误使用。
注意:我在您的描述中没有看到任何对 OAuth 2.0 客户端或资源服务器的引用,因此我假设您正试图直接在授权服务器上访问端点。如果不是这样,请告诉我。
OAuth 2.0 保护设置涉及三个应用程序:
- 授权服务器
- 资源服务器
- 客户
您的配置仅适用于#1(据我所知)。授权服务器包含两个过滤器链和一个单独的 oauth 客户端的配置。两个过滤器链执行以下操作:
- 授权服务器框架提供的安全端点
- 在使用授权端点 (
/oauth2/authorize
) 获取授权码之前保护用户将与之交互的登录端点,客户端稍后将使用该授权码获取访问令牌
您配置的范围将允许用户(资源所有者)授予 oauth 客户端使用访问令牌对资源服务器进行受保护调用的能力。只有当客户端调用资源服务器时,才会使用您配置的范围。当用户使用浏览器直接与授权服务器上的端点交互时,表单登录的配置正在发挥作用,正如我在评论中提到的那样,它使用数据库中的角色。
查看 SpringOne 2021 repository and presentation 以了解如何将应用程序从不安全的应用程序转变为安全的应用程序,然后了解我们如何将其转变为使用范围作为权限的资源服务器。
该演示文稿演示了所有三个应用程序,但重点放在资源服务器上,这与您使用范围作为权限尝试完成的目标非常匹配。
我已经使用新的 spring 授权服务器模块创建了一个授权服务器。我能够成功获取令牌,但是当我尝试将令牌用于 hasAuthority()
的受保护端点时,我收到禁止 403 错误。在我的 pom.xml
文件下面
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.erycoking</groupId>
<artifactId>auth-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>auth-service</name>
<description>Auth Service</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2021.0.0</spring-cloud.version>
<jhipster-dependencies.version>7.0.1</jhipster-dependencies.version>
<liquibase.version>4.6.1</liquibase.version>
</properties>
<dependencies>
<dependency>
<groupId>tech.jhipster</groupId>
<artifactId>jhipster-framework</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<!-- Jackson Configurations -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hppc</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>problem-spring-web</artifactId>
<version>0.26.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<!-- Inherited version from Spring Boot can't be used because of regressions -->
<version>${liquibase.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>${liquibase.version}</version>
</plugin>
</plugins>
</build>
</project>
下面是我的授权服务器配置
@Configuration(proxyBeanMethods = false)
public class AuthServerConfig {
private final DataSource dataSource;
private final AuthProperties authProps;
private final TokenSettings tokenSettings;
public AuthServerConfig(DataSource dataSource, AuthProperties authProps, TokenSettings tokenSettings) {
this.dataSource = dataSource;
this.authProps = authProps;
this.tokenSettings = tokenSettings;
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).build();
}
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
JdbcRegisteredClientRepository clientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
clientRepository.save(webClient());
return clientRepository;
}
private RegisteredClient webClient() {
return RegisteredClient.withId("98a9104c-a9c7-4d7c-ad03-ec61bcfeab36")
.clientId(authProps.getClientId())
.clientName(authProps.getClientName())
.clientSecret(authProps.getClientSecret())
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8080/authorized")
.scope("create").scope("read").scope("write").scope("update").scope("delete")
.tokenSettings(tokenSettings)
.build();
}
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
private static RSAKey generateRsa() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder()
.issuer(authProps.getIssuerUri())
.build();
}
}
这是我的安全配置
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig {
private final UserDetailsService userDetailsService;
public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/management/**").permitAll()
.antMatchers("/h2-console/**").permitAll()
//this does not work
.antMatchers(HttpMethod.POST, "/auth/user").hasAuthority(AuthoritiesConstants.ADMIN)
//this does not work
.antMatchers(HttpMethod.GET, "/auth/user").hasAuthority("SCOPE_read")
.anyRequest().authenticated()
.and()
.csrf().disable()
.headers().frameOptions().disable()
.and()
.formLogin(withDefaults())
.userDetailsService(userDetailsService);
return http.build();
}
@Bean
public PasswordEncoder delegatingPasswordEncoder() {
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("bcrypt", encoders);
passwordEncoder.setDefaultPasswordEncoderForMatches(new BCryptPasswordEncoder());
return passwordEncoder;
}
@Bean
public TokenSettings tokenSettings() {
return TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(1))
.refreshTokenTimeToLive(Duration.ofHours(24))
.build();
}
}
这是我的用户详情服务
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
return userRepository.findOneWithRolesByEmailIgnoreCase(login)
.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
if (!user.isActivated()) {
throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
user.getRoles().forEach(e -> {
grantedAuthorities.add(new SimpleGrantedAuthority(e.getName()));
e.getPermissions().forEach(p -> grantedAuthorities.add(new SimpleGrantedAuthority(p.getName())));
});
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), grantedAuthorities);
}
}
当使用令牌向需要单独身份验证的端点发出请求时,它会成功,但是当我尝试在需要角色的端点上使用它时,它会失败。
可能是什么问题?
根据评论中为阐明您的设置而进行的简短讨论,您似乎将授权服务器配置为常规安全应用程序,目的是将范围用作权限。但是,这是对授权服务器的错误使用。
注意:我在您的描述中没有看到任何对 OAuth 2.0 客户端或资源服务器的引用,因此我假设您正试图直接在授权服务器上访问端点。如果不是这样,请告诉我。
OAuth 2.0 保护设置涉及三个应用程序:
- 授权服务器
- 资源服务器
- 客户
您的配置仅适用于#1(据我所知)。授权服务器包含两个过滤器链和一个单独的 oauth 客户端的配置。两个过滤器链执行以下操作:
- 授权服务器框架提供的安全端点
- 在使用授权端点 (
/oauth2/authorize
) 获取授权码之前保护用户将与之交互的登录端点,客户端稍后将使用该授权码获取访问令牌
您配置的范围将允许用户(资源所有者)授予 oauth 客户端使用访问令牌对资源服务器进行受保护调用的能力。只有当客户端调用资源服务器时,才会使用您配置的范围。当用户使用浏览器直接与授权服务器上的端点交互时,表单登录的配置正在发挥作用,正如我在评论中提到的那样,它使用数据库中的角色。
查看 SpringOne 2021 repository and presentation 以了解如何将应用程序从不安全的应用程序转变为安全的应用程序,然后了解我们如何将其转变为使用范围作为权限的资源服务器。
该演示文稿演示了所有三个应用程序,但重点放在资源服务器上,这与您使用范围作为权限尝试完成的目标非常匹配。