Auth0 API + Spring:如何从成功的Auth0 API响应中验证用户身份

时间:2019-09-22 01:20:36

标签: java spring spring-boot jwt auth0

问题

我正在尝试创建一个使用前端的 Auth0 SPA + React 来验证用户身份而无需处理密码的应用程序。然后,我想保护使用 Spring 创建的 Auth 服务器创建的所有端点的安全。

只需澄清一下,流程将

Frontend ->
Auth through Auth0 ->
Redirect to users dashboard on frontend ->
Make HTTP request to endpoint sending JWT returned from Auth0 ->
Endpoint makes request to my Auth Server sending JWT returned from Auth0 ->
Auth server either either returns 401 or user object based on JWT ->
Endpoint grabs data specific to that user from DB ->
Returns data to frontend

我已经使用 Auth0 提供的快速入门指南设法使前端正常运行,但在弄清楚如何获得我的身份验证服务以验证用户。

我相信得出的结论是,我需要在 Auth0 上创建“ API ”并获取一个访问令牌,使用它来验证JWT,在这种情况下,它只是访问令牌,而不是前端包含的JWT。我也已经完成了这一部分,但是似乎没有办法知道用户是谁。测试此“ API ”时,发送有效请求后,我会返回

{
  "iss": "https://${username}.auth0.com/",
  "sub": "${alphanumericCharacters}@clients",
  "aud": "${ApiIdentifier}",
  "iat": ${issuedAt},
  "exp": ${expiresAt},
  "azp": "${alphanumericCharacters}",
  "gty": "client-credentials"
}

很高兴知道自己处在正确的轨道上,但似乎无法弄清楚该响应如何找到用户。

预期

我希望从我的验证服务

中验证 access_token 后能够识别特定用户

代码

我没有太多要显示的代码,但是我将提供 Auth Service

SecurityConfiguration.java

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${auth0.audience}")
    private String audience;

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuer;

    @Override
    public void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests()
                .mvcMatchers("/api/validate")
                    .authenticated()
                .and()
                .oauth2ResourceServer()
                    .jwt();
    }

    @Bean
    JwtDecoder jwtDecoder() {
        NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport)
                JwtDecoders.fromOidcIssuerLocation(issuer);

        OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

        jwtDecoder.setJwtValidator(withAudience);

        return jwtDecoder;
    }

}

AudienceValidator.java

public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
    private final String audience;

    public AudienceValidator(String audience) {
        this.audience = audience;
    }

    public OAuth2TokenValidatorResult validate(Jwt jwt) {
        OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);

        if (jwt.getAudience().contains(audience)) {
            return OAuth2TokenValidatorResult.success();
        }
        return OAuth2TokenValidatorResult.failure(error);
    }
}

ValidateController.java

@RestController
@RequestMapping("/api/validate")
public class ValidateController {

    @GetMapping
    public boolean validate() {
        return true;  // only returns if successfully authed
    }

}

1 个答案:

答案 0 :(得分:1)

在阅读了文档之后,我找到了解决方案。

事实证明,我不需要在 Auth0 上创建“ API ”,而是需要使用我的 Applications 端点( s)来自 Auth0 Auth0 根据您的帐户提供许多终结点,只要您可以,就可以从任何应用程序(CLI,服务器,客户端等)中利用这些终结点:

  • 发出HTTP请求
  • 提供凭据

因此获取用户信息的方法是explained here

数据流

使用我的项目的auth /数据流差不多:

  • 使用前端上的@auth0/auth0-spa-js,您可以在使用getTokenSilently() method的身份验证成功后,抓住用户访问令牌

  • 将HTTP请求发送到您的 Rest服务

  • Rest服务将该令牌发送到您的 Auth Service

  • 身份验证服务将带有https://myAuth0Username.auth0.com/userinfo标头的 GET 请求发送到Authorization: Bearer ${access_token}Example

  • 如果已成功从 Auth0

    进行身份验证
    • 返回您的用户信息,例如“姓名”,“电子邮件”等。
  • 其他

    • 返回403禁止的HTTP状态
  • 身份验证服务,然后将用户对象返回给其他服务

  • Rest Service 然后为该端点执行必要的逻辑(数据库查询,另一个HTTP请求等)

用于验证令牌并返回用户的Auth Service端点示例

ValidateController.java

package x.SpringTodo_Auth.Controllers;

import x.SpringTodo_Auth.Models.User;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/api/validate")
public class ValidateController {

    @GetMapping
    public Object validate() {
        // Create and set the "Authorization" header before sending HTTP request
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + access_token);
        HttpEntity<String> entity = new HttpEntity<>("headers", headers);

        // Use the "RestTemplate" API provided by Spring to make the HTTP request
        RestTemplate restTemplate = new RestTemplate();
        Object user = restTemplate.exchange("https://myAuth0Username.auth0.com/userinfo", HttpMethod.POST, entity, User.class);
        return user;
    }

}

User.java (这是作为最后一个参数传递给restTemplate.exchange(...)方法的类

package x.SpringTodo_Auth.Models;

public class User {

    private String sub;
    private String given_name;
    private String family_name;
    private String nickname;
    private String name;
    private String picture;
    private String locale;
    private String updated_at;
    private String email;
    private boolean email_verified;

    // Getters/setters (or you can use Lombok)
}