如何扩展Micronaut CustomJWTClaimsSetGenerator返回全部用户属性
JWT认证扩展返回自定义用户属性实现方案
现有代码基础
1. CustomDelegatingAuthenticationProvider 类
@Singleton @Replaces(value = DelegatingAuthenticationProvider.class) public class CustomDelegatingAuthenticationProvider extends DelegatingAuthenticationProvider { /** * @param userFetcher 从持久层拉取用户信息 * @param passwordEncoder 用于校验明文密码与加密密码是否匹配 * @param authoritiesFetcher 拉取指定用户的权限集合 */ 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); } }
2. 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()); } } }
3. HDSUser 自定义用户类
@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; } }
需求说明
当前JWT认证成功后返回给客户端的响应仅包含用户名,需要额外返回用户ID等自定义用户属性。
实现方案
按你需要返回属性的场景选择对应修改方式:
场景1:自定义属性写入JWT Token载荷
你现有的CustomJWTClaimsSetGenerator已经实现了userId写入JWT载荷的逻辑,如果需要新增更多用户属性,按以下步骤操作:
- 在
HDSUser类中新增对应成员变量,以及对应的getter、setter方法,示例新增手机号字段:
@CompileStatic public class HDSUser extends UserDetails { private long id; // 新增手机号字段 private String phone; public HDSUser(String username, Collection<String> roles, long id, String phone) { super(username, roles); this.id = id; this.phone = phone; } // 原有getter setter省略 public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }
- 修改
CustomJWTClaimsSetGenerator的populateWithUserDetails方法,追加自定义字段写入逻辑:
protected void populateWithUserDetails(JWTClaimsSet.Builder builder, UserDetails userDetails) { super.populateWithUserDetails(builder, userDetails); if (userDetails instanceof HDSUser) { HDSUser hdsUser = (HDSUser) userDetails; builder.claim("userId", hdsUser.getId()); // 追加手机号字段 builder.claim("phone", hdsUser.getPhone()); } }
注意:不要将密码、身份证号等敏感信息写入JWT载荷,JWT仅做Base64编码,明文可直接解析。
场景2:自定义属性直接在认证接口响应体返回
如果不需要把属性放到Token里,只需要在/login等认证接口的响应体中直接返回给客户端,修改CustomDelegatingAuthenticationProvider的createSuccessfulAuthenticationResponse方法,使用AuthenticationResponse.success的带附加属性重载方法即可:
@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 -> { HDSUser hdsUser = new HDSUser(user.getUsername(), authorities, user.getId(), user.getPhone()); // 组装要返回的自定义属性 Map<String, Object> extAttrs = new HashMap<>(); extAttrs.put("userId", hdsUser.getId()); extAttrs.put("phone", hdsUser.getPhone()); // 生成带自定义属性的认证成功响应 return AuthenticationResponse.success(hdsUser.getUsername(), extAttrs, hdsUser); }); } return super.createSuccessfulAuthenticationResponse(authenticationRequest, userState); }
补充说明
如果需要后续请求解析JWT时,能直接从上下文拿到完整的HDSUser对象,你需要额外自定义TokenAuthenticationFetcher实现类,从JWT的载荷中取出自定义字段,重新组装成HDSUser对象注入到请求上下文。
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

