扩展 Micronaut CustomJWTClaimsSetGenerator 以提供所有属性

Extend Micronaut CustomJWTClaimsSetGenerator to provide all attributes

我有以下两个 class 提供 JWT 身份验证机制。

CustomDelegatingAuthenticationProvider

@Singleton
@Replaces(value = DelegatingAuthenticationProvider.class)
public class CustomDelegatingAuthenticationProvider extends DelegatingAuthenticationProvider {
    /**
     * @param userFetcher        Fetches users from persistence
     * @param passwordEncoder    Collaborator which checks if a raw password matches an encoded password
     * @param authoritiesFetcher Fetches authorities for a particular user
     */
    public CustomDelegatingAuthenticationProvider(UserFetcher userFetcher, PasswordEncoder passwordEncoder, AuthoritiesFetcher authoritiesFetcher) {
        super(userFetcher, passwordEncoder, authoritiesFetcher);
    }

    @Override
    protected Publisher<AuthenticationResponse> createSuccessfulAuthenticationResponse(AuthenticationRequest authenticationRequest, UserState userState) {

        if (userState instanceof UserMember) {
            UserMember user = (UserMember) userState;
            return Flowable
                    .fromPublisher(authoritiesFetcher.findAuthoritiesByUsername(user.getUsername()))
                    .map(authorities -> new HDSUser(user.getUsername(), authorities, user.getId()));
        }
        return super.createSuccessfulAuthenticationResponse(authenticationRequest, userState);
    }
}

CustomJWTClaimsSetGenerator

@Singleton
@Replaces(value = JWTClaimsSetGenerator.class)
public class CustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator {

    CustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, @Nullable JwtIdGenerator jwtIdGenerator, @Nullable ClaimsAudienceProvider claimsAudienceProvider) {
        super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider);
    }

    protected void populateWithUserDetails(JWTClaimsSet.Builder builder, UserDetails userDetails) {
        super.populateWithUserDetails(builder, userDetails);

        if (userDetails instanceof HDSUser) {
            builder.claim("userId", ((HDSUser) userDetails).getId());
        }
    }

}

对客户端的默认响应如下所示:

我的问题。如何将 class 扩展到 return 所有用户属性?除了用户名,我还想拥有用户 ID。

更新

收集DB id

的HDS用户class
@CompileStatic
public class HDSUser  extends UserDetails {

    private long id;

    public HDSUser(String username, Collection<String> roles, long id) {
        super(username, roles);
        this.id = id;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
}

要扩展返回的数据,您需要扩展(实现自定义)TokenRenderer 以及 AccessRefreshToken 的自定义版本。 作为一个简单的示例,请参阅以下代码片段,它将使用 userId 字段扩展默认访问令牌负载。

首先,创建自定义 AccessRefreshToken class,其中包含其他必填字段。

@Introspected
@Getter
@Setter
public class CustomAccessRefreshToken extends BearerAccessRefreshToken {
    
    // the new field which will be in the response
    private String userId;

    public CustomAccessRefreshToken(String username,
                                Collection<String> roles,
                                Integer expiresIn,
                                String accessToken,
                                String refreshToken,
                                String tokenType
    ) {
        super(username, roles, expiresIn, accessToken, refreshToken, tokenType);
    }
  
}

接下来,我们将需要一个 TokenRenderer,底层子系统将使用它来生成我们的自定义令牌。

@Singleton
@Replaces(value = BearerTokenRenderer.class)
public class CustomTokenRenderer implements TokenRenderer {
    private static final String BEARER_TOKEN_TYPE = HttpHeaderValues.AUTHORIZATION_PREFIX_BEARER;

    @Override
    public AccessRefreshToken render(Integer expiresIn, String accessToken, @Nullable String refreshToken) {
    return new AccessRefreshToken(accessToken, refreshToken, BEARER_TOKEN_TYPE, expiresIn);
    }

    @Override
    public AccessRefreshToken render(Authentication authentication, Integer expiresIn, String accessToken, @Nullable String refreshToken) {
        CustomAccessRefreshToken token =  new CustomAccessRefreshToken(authentication.getName(), authentication.getRoles(), expiresIn, accessToken, refreshToken, BEARER_TOKEN_TYPE);
        // here just take the user data from Authentication object or access any other service
        token.setUserId("Some user id");
        return token;
    }
}

就是这样 )) 只需按照您想要的方式实施 render() 方法,并根据需要添加尽可能多的自定义字段。

给定示例的响​​应如下所示

{
    "username": "sherlock",
    "userId": "Some user id",
    "access_token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzaGVybG9jayIsIm5iZiI6MTYzNjk5MTgzMSwicm9sZXMiOltdLCJpc3MiOiJtaWNyb25hdXRndWlkZSIsImV4cCI6MTYzNjk5NTQzMSwiaWF0IjoxNjM2OTkxODMxfQ.Cat1CTsUZkCj-OHGafiefNm1snPsALoaNw9y2xwF5Pw",
    "token_type": "Bearer",
    "expires_in": 3600
}

如果您使用的是旧版本的 Micronaut v1.x,TokenRenderer 将如下所示。

@Singleton
@Replaces(value = BearerTokenRenderer.class)
public class CustomTokenRenderer implements TokenRenderer {
    private static final String BEARER_TOKEN_TYPE = HttpHeaderValues.AUTHORIZATION_PREFIX_BEARER;

    public AccessRefreshToken render(Integer expiresIn, String accessToken, String refreshToken) {
        return new AccessRefreshToken(accessToken, refreshToken, BEARER_TOKEN_TYPE, expiresIn);
    }

    public AccessRefreshToken render(UserDetails userDetails, Integer expiresIn, String accessToken, String refreshToken) {
        CustomAccessRefreshToken token =  new CustomAccessRefreshToken(userDetails.getUsername(), userDetails.getRoles(), expiresIn, accessToken, refreshToken, BEARER_TOKEN_TYPE);
        token.setUserId("Some user id, Some user id");
        return token;
    }
}