如何从 Discord OAUTH2 获取响应并将其转换为我自己的 UserDetails,我可以在整个代码中使用它

Posted

技术标签:

【中文标题】如何从 Discord OAUTH2 获取响应并将其转换为我自己的 UserDetails,我可以在整个代码中使用它【英文标题】:How do I take the response from Discord OAUTH2 and translate it in to my own UserDetails that I can use throughout my code 【发布时间】:2021-10-05 10:22:03 【问题描述】:

我正在为我的应用程序使用 Spring Boot + Spring Security OAUTH 2。我想使用 Spring OAUTH2 来“使用 Discord 登录”,然后将 discord 在身份验证成功时提供的信息解析到我自己的实现 UserDetails 的 JPA 实体中。另外,我想实现我自己的 UserDetailsService 来返回我创建的自定义用户实体。

基本上,我只希望 Discord 提供有关用户的唯一信息,以便我可以根据响应构建自己的 Account 实体。

我已经阅读了以下文章,但我仍然不明白该怎么做:

https://www.baeldung.com/spring-security-oauth-principal-authorities-extractor

https://www.devglan.com/spring-security/spring-oauth2-role-based-authorization

另外,这是我的 OAuth2 设置:

安全配置(WebSecurityConfigurerAdapter):

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Autowired
    private AuthSuccessHandler authSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception 
            http
                    .authorizeRequests()
                    .antMatchers(HttpMethod.GET, "/").permitAll()
                    .antMatchers(HttpMethod.GET, "/favicon.ico").permitAll()
                    .antMatchers(HttpMethod.GET, "/web/**").permitAll()
                    .antMatchers(HttpMethod.GET, "/oauth2/authorization/discord").anonymous()
                    .antMatchers(HttpMethod.GET, "/login").anonymous()
                    .antMatchers(HttpMethod.GET, "/logout").permitAll()
                    .and()
                    .logout()
                    .logoutSuccessUrl("/")
                    .and()
                    .oauth2Login()
                    .successHandler(authSuccessHandler)
                    .tokenEndpoint().accessTokenResponseClient(accessTokenResponseClient())
                    .and()
                    .userInfoEndpoint().userService(userService());
    

    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() 
        DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient();

        client.setRequestEntityConverter(new OAuth2AuthorizationCodeGrantRequestEntityConverter() 
            @Override
            public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest oauth2Request) 
                return addUserAgentHeader(Objects.requireNonNull(super.convert(oauth2Request)));
            
        );

        return client;
    

    @Bean
    public OAuth2UserService<OAuth2UserRequest, OAuth2User> userService() 
        DefaultOAuth2UserService service = new DefaultOAuth2UserService();

        service.setRequestEntityConverter(new OAuth2UserRequestEntityConverter() 
            @Override
            public RequestEntity<?> convert(OAuth2UserRequest userRequest) 
                return addUserAgentHeader(
                        Objects.requireNonNull(super.convert(userRequest)));
            
        );

        return service;
    

    private RequestEntity<?> addUserAgentHeader(RequestEntity<?> request) 
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(request.getHeaders());
        headers.add(HttpHeaders.USER_AGENT, "Mozilla/5.0 (X11; Linux x86_64; rv:33.0) Gecko/20100101 Firefox/33.0");

        return new RequestEntity<>(request.getBody(), headers, request.getMethod(), request.getUrl());
    

AuthSuccessHandler (AuthenticationSuccessHandler) 正如您所看到的,我正在尝试通过根据 discord 提供的信息保存 JPA 实体并仅从数据库中提取而不是获取当前用户来解决此问题,这会阻止我使用角色和权限。

@Component
@RequiredArgsConstructor
@Slf4j
public class AuthSuccessHandler implements AuthenticationSuccessHandler 

    private final AccountRepository accountRepository;
    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();


    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException 
        AuthenticationSuccessHandler.super.onAuthenticationSuccess(request, response, chain, authentication);
    

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException 
        OAuth2User oAuth2User = ((OAuth2AuthenticationToken) authentication).getPrincipal();
//        oAuth2User.getAttributes().forEach((s, o) -> 
//            System.out.println(s + " : " + o);
//        );

        Long discordId = Long.parseLong(Objects.requireNonNull(oAuth2User.getAttribute("id")));
        Optional<Account> accountOptional = accountRepository.findAccountByDiscordId(discordId);
        if (!httpServletResponse.isCommitted()) 
            if (accountOptional.isEmpty()) 
                accountRepository.save(getAccountFromOAuth2User(oAuth2User));
            
            redirectStrategy.sendRedirect(httpServletRequest, httpServletResponse, "/dashboard");
         else 
            log.warn("Response committed");
        

    

    private Account getAccountFromOAuth2User(OAuth2User oAuth2User) 
        return new Account(Long.parseLong(oAuth2User.getAttribute("id")),oAuth2User.getAttribute("username"), oAuth2User.getAttribute("discriminator"));
    


application.properties

spring.security.oauth2.client.registration.discord.client-name=Discord
spring.security.oauth2.client.registration.discord.client-id=**CLIENT**
spring.security.oauth2.client.registration.discord.client-secret=**SECRET**
spring.security.oauth2.client.registration.discord.clientAuthenticationMethod=post
spring.security.oauth2.client.registration.discord.authorizationGrantType=authorization_code
spring.security.oauth2.client.registration.discord.redirect-uri=http://localhost:8081/login/oauth2/code/discord
spring.security.oauth2.client.registration.discord.scope=identify

spring.security.oauth2.client.provider.discord.authorization-uri=https://discordapp.com/api/oauth2/authorize
spring.security.oauth2.client.provider.discord.token-uri=https://discordapp.com/api/oauth2/token
spring.security.oauth2.client.provider.discord.user-info-uri=https://discordapp.com/api/users/@me
spring.security.oauth2.client.provider.discord.user-name-attribute=username

对此的任何帮助将不胜感激。

提前致谢!

编辑: 使用的大部分代码来自这个 GitHub 存储库:

https://github.com/Samurus/spring-boot-discord-oauth-example

【问题讨论】:

【参考方案1】:

我通过使用CustomUserTypesOAuth2UserService 解决了这个问题,这让我可以使用实现OAuth2User 的自定义类

【讨论】:

以上是关于如何从 Discord OAUTH2 获取响应并将其转换为我自己的 UserDetails,我可以在整个代码中使用它的主要内容,如果未能解决你的问题,请参考以下文章

Discord OAuth2 从确切的公会获取用户角色

如何从 api 获取 json 数据并将其以嵌入形式发送到 discord.py 中?

如何获取表单数据并将其发送到 discord webhook?

OAuth2:Discord API 总是以 "error": "invalid_grant" 响应

Discord 使用 url-query 中的“代码”发送 Oauth2 重定向 url。如何在我的谷歌脚本中获取该代码

Discord OAuth2:'请求中缺少“代码”'