Spring Boot中如何将自定义UserInfo类存入Security Context
如何将自定义UserInfo存入Spring Security Context
要把自定义的UserInfo类存入Spring Security的Security Context,核心是让Spring Security能够识别这个类作为认证主体——通常我们会通过实现UserDetails接口来达成这个目的,然后构建合法的Authentication对象并注入上下文。下面是具体的实现步骤:
1. 让UserInfo适配UserDetails接口
Spring Security的认证体系依赖UserDetails来描述用户信息,所以我们需要把UserInfo转换成UserDetails类型,有两种可选方式:
方式一:直接让UserInfo实现UserDetails
修改你的UserInfo类,实现UserDetails接口并完成必要方法的实现:
@Getter @Setter @Builder @AllArgsConstructor @NoArgsConstructor public class UserInfo implements Serializable, UserDetails { private List<String> role = new ArrayList<>(); private String username; private String email; @Override public Collection<? extends GrantedAuthority> getAuthorities() { // 将自定义的role列表转换为Spring Security可识别的GrantedAuthority集合 return role.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); } @Override public String getPassword() { // 由于我们是从认证服务器获取的用户信息,这里不需要密码,返回null即可 return null; } @Override public boolean isAccountNonExpired() { return true; // 根据你的业务需求调整,默认账号未过期 } @Override public boolean isAccountNonLocked() { return true; // 默认账号未锁定 } @Override public boolean isCredentialsNonExpired() { return true; // 默认凭证未过期 } @Override public boolean isEnabled() { return true; // 默认账号启用 } }
方式二:创建UserDetails适配器(解耦原有类)
如果不想修改原有的UserInfo类(比如它是第三方依赖类,或者不想和Spring Security强耦合),可以创建一个适配器类:
public class UserInfoUserDetails implements UserDetails { private final UserInfo userInfo; public UserInfoUserDetails(UserInfo userInfo) { this.userInfo = userInfo; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return userInfo.getRole().stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); } @Override public String getPassword() { return null; } @Override public String getUsername() { return userInfo.getUsername(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } // 提供方法获取原始UserInfo对象,方便后续业务使用 public UserInfo getUserInfo() { return userInfo; } }
2. 在JwtAuthenticationFilter中构建Authentication并注入上下文
修改你的getJwtFromRequest方法,在成功获取并验证UserInfo后,构建Authentication对象并存入SecurityContextHolder:
如果用方式一(UserInfo直接实现UserDetails):
private void getJwtFromRequest(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String bearerToken = request.getHeader("Authorization"); if (!StringUtils.hasText(bearerToken) || !bearerToken.startsWith("Bearer ")) { throw new AccessTokenMissingException("No access token found in request headers"); } // Call auth server to validate token try { ResponseEntity<String> result = authenticationService.getUserInfo(bearerToken.substring(7)); // 先判断响应状态码,再解析用户信息(更合理的顺序) if (!result.getStatusCode().is2xxSuccessful()) { throw new InvalidAccessTokenException("Invalid access token"); } UserInfo user = new ObjectMapper().readValue(result.getBody(), UserInfo.class); System.out.println(user.toString()); // 构建Authentication对象 Authentication authentication = new UsernamePasswordAuthenticationToken( user, // principal:认证主体,这里UserInfo已经是UserDetails类型 null, // credentials:JWT认证已完成密码验证,这里传null即可 user.getAuthorities() // 用户权限集合 ); // 将认证信息存入Security Context SecurityContextHolder.getContext().setAuthentication(authentication); } catch (HttpClientErrorException.Unauthorized | IOException e) { throw new InvalidAccessTokenException("Invalid access token"); } filterChain.doFilter(request, response); }
如果用方式二(适配器):
把构建Authentication的部分替换成:
UserInfo user = new ObjectMapper().readValue(result.getBody(), UserInfo.class); UserInfoUserDetails userDetails = new UserInfoUserDetails(user); Authentication authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); SecurityContextHolder.getContext().setAuthentication(authentication);
关键说明
UsernamePasswordAuthenticationToken是Spring Security中最常用的Authentication实现类,这里我们用它来封装认证信息。SecurityContextHolder.getContext().setAuthentication(authentication)是将认证信息存入上下文的核心操作,后续Spring Security的其他组件(比如方法权限注解@PreAuthorize)就能从上下文中获取到用户信息和权限了。- 注意调整代码中状态码判断和用户信息解析的顺序,避免在响应失败时去解析无效的响应体。
内容的提问来源于stack exchange,提问作者shadow
相关产品推荐
相关产品推荐

