将 OAuth2 与 Spring Security 集成,同时保留自定义用户对象

问题描述 投票:0回答:1

我们正在将 Spring Security 应用程序从用户名/密码身份验证转换为 OAuth2。我们已成功将 OAuth2 登录添加到安全链,但我们在安全上下文中面临用户对象的问题。

这是我们当前的设置:

用户对象:

public class CustomUser implements UserDetails {
    private Long id;
    private String username;
    private Set<CustomRole> roles;
    // Many more fields, getters, setters, etc.
}
public class UserService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            return getUserByLoginname(username);
        } catch (NotFoundException e) {
            throw new UsernameNotFoundException("");
        }
    }

    public CustomUser getUserByLoginname(String identifier) throws NotFoundException {
        // Load User from database
    }
}

然后在很多地方我们使用以下片段来检索登录的用户:

@GetMapping(value = "/XXX")
public void XXX() {
    CustomUser loggedInUser = (CustomUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
// ... 
}

现在我们希望集成 OAuth2,同时保留我们的 CustomUser 模型。我们的目标是仅将 OAuth2 用于身份验证目的,而不是从 OAuth 流程中检索任何权限或角色。相反,我们希望在我们自己的数据库中维护和管理用户角色。为了实现这一点,我已将 oauth2Login 合并到安全配置中,如下所示:

@Bean
protected SecurityFilterChain configure(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository)
        throws Exception {
    http
        .cors(c -> corsConfigurationSource())
            .authorizeHttpRequests(requests -> requests
                .requestMatchers(AntPathRequestMatcher.antMatcher("/XXX"),
                        AntPathRequestMatcher.antMatcher("/XXXXX"))
                    .permitAll()

                .anyRequest().authenticated())

            .oauth2Login(o -> o.authorizationEndpoint(end -> end
                    .authorizationRequestResolver(new CustomAuthorizationRequestResolver(clientRegistrationRepository))))

            .logout(l -> l.logoutSuccessUrl("/logoutsite").permitAll())

            .sessionManagement(management -> management.maximumSessions(-1).sessionRegistry(sessionRegistry()))
            .headers(headers -> headers.referrerPolicy(policy -> policy.policy(ReferrerPolicy.NO_REFERRER)))
            .csrf(csrf -> csrf.disable());
    return http.build();
}

这也行,认证成功了。但现在我们遇到的问题是,在安全上下文中我们有一个 DefaultOidcUser 而不是我们的 customUser 对象。

我考虑在 UsernamePasswordAuthenticationFilter 之后添加一个过滤器,以用我们的自定义对象替换 SecurityContext 中的 User 对象。然而,这种方法感觉有点肮脏。

我想在 UsernamePasswordAuthenticationFilter 之后添加一个过滤器,用新的过滤器替换 SecurityContext 中的 Userobject。但这似乎有点肮脏。我觉得必须有更好的方法。但到目前为止我尝试过的所有方法(例如使用 OAuthUserService 或 OidcUserService)仍然会产生某种 OAuth 对象。

我的问题是:如何仅使用 OAuth 进行身份验证,然后仅将我的 CustomUser 对象加载到安全上下文中,同时忽略 OAuth 的任何授权和权限?

java spring spring-security oauth spring-security-oauth2
1个回答
0
投票

如果您阅读了 Spring Security 文档,这正是

GrantedAuthoritiesMapper
的用途。它记录在文档中here

这是文档中的代码示例:

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .oauth2Login(oauth2 -> oauth2
                .userInfoEndpoint(userInfo -> userInfo
                    .userAuthoritiesMapper(this.userAuthoritiesMapper())
                    ...
                )
            );
        return http.build();
    }

    private GrantedAuthoritiesMapper userAuthoritiesMapper() {
        return (authorities) -> {
            Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

            authorities.forEach(authority -> {
                if (OidcUserAuthority.class.isInstance(authority)) {
                    OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;

                    OidcIdToken idToken = oidcUserAuthority.getIdToken();
                    OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();

                    // Map the claims found in idToken and/or userInfo
                    // to one or more GrantedAuthority's and add it to mappedAuthorities

                } else if (OAuth2UserAuthority.class.isInstance(authority)) {
                    OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;

                    Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();

                    // Map the attributes found in userAttributes
                    // to one or more GrantedAuthority's and add it to mappedAuthorities

                }
            });

            return mappedAuthorities;
        };
    }
}

在文档中提供的代码示例中,他们手动从 JWT 等中提取权限,并返回带有新的 GrantedAuthorities 的地图,但在您的情况下,我相信您会调用数据库并获取所需的权限(角色)和与您需要的权威机构一起构建地图并将其返回。

© www.soinside.com 2019 - 2024. All rights reserved.