spring - 如何在Spring OAuth2资源服务器中使用自定义UserDetailSservice?

标签 spring spring-boot spring-security spring-oauth2

我正在使用 Spring Boot (2.3.4.RELEASE) 来实现充当 OAuth2 资源服务器的 Web 服务。到目前为止,我能够保护所有端点并确保存在有效的 token 。在下一步中,我想使用 Spring Method Security。第三步是填充自定义用户详细信息(通过 UserDetailsS​​ervice)。

如何正确配置 Spring Method Security?

我无法(正确)启用 Spring Method Security。我将实体保存在数据库中,并通过 MutableAclService 设置权限。创建新资源没有问题。

我在读取实体时收到以下错误

o.s.s.acls.AclPermissionEvaluator        : Checking permission 'OWNER' for object 'org.springframework.security.acls.domain.ObjectIdentityImpl[Type: io.mvc.webserver.repository.entity.ProjectEntity; Identifier: my-second-project]'
o.s.s.acls.AclPermissionEvaluator        : Returning false - no ACLs apply for this principal
o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter@120d62d, returned: -1
o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.access.vote.RoleVoter@429b9eb9, returned: 0
o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.access.vote.AuthenticatedVoter@65342bae, returned: 0
o.s.web.servlet.DispatcherServlet        : Failed to complete request: org.springframework.security.access.AccessDeniedException: Zugriff verweigert
o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is not anonymous); delegating to AccessDeniedHandler

我使用以下表达式:

@PreAuthorize("hasPermission(#projectKey, 'io.mvc.webserver.repository.entity.ProjectEntity', 'OWNER')")
ProjectEntity findByKey(String projectKey);

如何提供自定义用户详情服务?

据我了解,Spring Security 根据经过身份验证的用户(通过 OAuth2 JWT)设置 SecurityContext。我想根据 token 中识别的用户设置自定义用户对象(主体)。但仅仅提供一个 UserDetailsS​​ervice 类型的 Bean 似乎不起作用。我的 UserDetailsS​​ervice 从未被调用...

安全配置

@Configuration
@EnableWebSecurity
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .cors().and()
                .httpBasic().disable()
                .formLogin().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests(authorize -> authorize
                    .antMatchers("/actuator/**").permitAll() // TODO: Enable basic auth for actuator
                    .anyRequest().authenticated()
                )
                .oauth2ResourceServer().jwt();
    }
}

ACL配置

@Configuration
public class AclConfiguration {
    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler(PermissionEvaluator permissionEvaluator) {
        DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(permissionEvaluator);

        return expressionHandler;
    }

    @Bean
    public PermissionEvaluator permissionEvaluator(PermissionFactory permissionFactory, AclService aclService) {
        AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService);
        permissionEvaluator.setPermissionFactory(permissionFactory);

        return permissionEvaluator;
    }

    @Bean
    public PermissionFactory permissionFactory() {
        return new DefaultPermissionFactory(MvcPermission.class);
    }

    @Bean
    public MutableAclService aclService(LookupStrategy lookupStrategy, AclCache aclCache, AclRepository aclRepository) {
        return new MongoDBMutableAclService(aclRepository, lookupStrategy, aclCache);
    }

    @Bean
    public AclAuthorizationStrategy aclAuthorizationStrategy() {
        return new AclAuthorizationStrategyImpl(
                new SimpleGrantedAuthority("ROLE_ADMIN"));
    }

    @Bean
    public PermissionGrantingStrategy permissionGrantingStrategy() {
        return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
    }

    @Bean
    public AclCache aclCache(PermissionGrantingStrategy permissionGrantingStrategy,
                             AclAuthorizationStrategy aclAuthorizationStrategy,
                             EhCacheFactoryBean ehCacheFactoryBean) {
        return new EhCacheBasedAclCache(
                ehCacheFactoryBean.getObject(),
                permissionGrantingStrategy,
                aclAuthorizationStrategy
        );
    }

    @Bean
    public EhCacheFactoryBean aclEhCacheFactoryBean(EhCacheManagerFactoryBean ehCacheManagerFactoryBean) {
        EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
        ehCacheFactoryBean.setCacheManager(ehCacheManagerFactoryBean.getObject());
        ehCacheFactoryBean.setCacheName("aclCache");
        return ehCacheFactoryBean;
    }

    @Bean
    public EhCacheManagerFactoryBean aclCacheManager() {
        EhCacheManagerFactoryBean cacheManagerFactory = new EhCacheManagerFactoryBean();
        cacheManagerFactory.setShared(true);
        return cacheManagerFactory;
    }

    @Bean
    public LookupStrategy lookupStrategy(MongoTemplate mongoTemplate,
                                         AclCache aclCache,
                                         AclAuthorizationStrategy aclAuthorizationStrategy) {
        return new BasicMongoLookupStrategy(
                mongoTemplate,
                aclCache,
                aclAuthorizationStrategy,
                new ConsoleAuditLogger()
        );
    }
}

依赖关系

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-acl</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.11</version>
</dependency>

最佳答案

简单的 Spring 原生解决方案

如果要使用自定义用户对象,需要实现和配置Converter<Jwt,? extends AbstractAuthenticationToken> .

    @Bean
    public SecurityWebFilterChain filterChain(
        ServerHttpSecurity http,
        MyConverter converter
    ) {
        http.oauth2ResourceServer()
            .jwt()
                .jwtAuthenticationConverter(converter);
        return http.build();
    }

如果您有 UserDetailsS​​ervice,您应该考虑使用详细信息而不是主体。根据提供的内容创建一个新的 JwtAuthenticationToken,解析您的权限并使用 jwt.setDetails(myUserDetails) 。 如果您需要自己实现 UserDetailsS​​ervice,那么仅实现转换器并使用您自己的 AbstractAuthenticationToken 变体而不是替换主体可能会更容易。

展望 future /考虑

Jwt 是一种无状态身份验证机制。您的转换器将用于每个经过身份验证的请求。您要小心 api 或数据库查找等副作用。 如果您想从数据库或 API 查询其他用户信息,请考虑创建一个新 token 来加密应用程序所需的所有内容。您还可以考虑缓存。 session 缓存可以是一个简单、值得的开始,因为缓存主要是为了提高性能。对于无国籍来说,这是一个灰色地带。

关于当前接受的答案

已接受的答案存在各种问题。最大的问题是,它用自定义身份验证过滤器替换了整个 Spring 资源服务器 jwt 逻辑。答案甚至提到,您需要连接各种逻辑,例如 jwt 解析,这需要对 jwt 或 spring 内部结构有更深入的了解,因此只有当您需要对身份验证 header 处理进行强有力的控制时才有意义,但如果您只是想要使用您的自定义身份验证类。

关于spring - 如何在Spring OAuth2资源服务器中使用自定义UserDetailSservice?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64626686/

相关文章:

java - 使用 Spring Security 后 $http 请求给出 403?

java - 使用 Spring @SubscribeMapping 获取当前用户

java - Jade4J:没有这样的文件或目录

spring - Spring 应用程序上下文模式中的错误

java - 如何在 Spring Boot 中将特定类记录到另一个日志文件

java - 如何删除基本身份验证的弹出窗口

java - Spring Security 中 JSON 的主要对象

java - Hibernate saveorupdate 或 merge 用于保存/更新一对多关系对象

java - 无法使用Gradle分发获取 'BuildEnvironment'类型的模型

spring - 在冲突(409)响应的 JpaRepository 中添加冲突实体