我想用JWT保护REST服务器。为此,我使用this tutorial。一切正常,除了一件事-当用户已经登录但我要在私有控制器中获得授权用户,但是getUserInfo方法接收到等于null的用户。
这是私有控制器AccountController:
@RestController
@RequestMapping("/private")
public class AccountController {
@GetMapping("/getUserInfo")
public @ResponseBody
String getUserInfo(@AuthenticationPrincipal final UserDetails user) {// user == null
return user.getUsername();
}
}
WebSecurity:
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private AccountDetailsServiceImpl accountDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(AccountDetailsServiceImpl accountDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.accountDetailsService = accountDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(accountDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
AccountDetailsServiceImpl:
@Service
public class AccountDetailsServiceImpl implements UserDetailsService {
private AccountRepository applicationUserRepository;
public AccountDetailsServiceImpl(AccountRepository applicationUserRepository) {
this.applicationUserRepository = applicationUserRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Account> applicationUser = applicationUserRepository.findByUsername(username);
if (applicationUser == null) {
throw new UsernameNotFoundException(username);
}
User user = new User(applicationUser.get().getUsername(), applicationUser.get().getPassword(), emptyList());
return user;
}
}
帐户:
@Entity(name = "Account")
@Table(name = "account")
public class Account implements UserDetails {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private Account() { }
public Account(final String username, final String password) {
this.username = username;
this.password = password;
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return new ArrayList<>();
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public Long getId() {
return id;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
那是什么问题,如何使用AuthenticationPrincipal
获得授权用户?
UPD:
我尝试使用
String getUserInfo(@AuthenticationPrincipal final Object user)
在我的AccountController
中(我将user
的类型更改为Object
),并得到了不是空对象。之后,我尝试使用user.getClass()
获得真实的对象类型并得到String
。接下来,我尝试将user
签名中的参数String
的类型更改为getUserInfo
,并用用户名获得了String
。因此,我可以获得用户信息,但类型不合适。我也尝试使用Account
类型,但也得到了null
。
因此,现在的主要问题是如何使用user
获得Account
类型的@AuthenticationPrincipal
?
UPD2:
现在我也意识到getUserInfo
获得了UsernamePasswordAuthenticationToken
对象,所以我认为我需要更改JWTAuthorizationFilter
,但是SecurityContextHolder.getContext().setAuthentication()
是否需要{{1 }}?
UPD3:
JWTAuthorizationFilter:
UsernamePasswordAuthenticationToken
UPD4:
JWTAuthenticationFilter.doFilterInternal():
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser().setSigningKey(KEY).parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody().getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
此方法的更新版本抛出@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
authentication.setDetails(authenticationDetailsSource.buildDetails(req));
Authentication authResult = getAuthenticationManager().authenticate(authentication); //BadCredentialsException: Bad credentials
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(req, res);
}