我根据参考文档设置了 Spring Security 应用程序,经过数小时的故障排除后,我继续将 null @AuthenticationPrincipal 传递到我的控制器中。
身份验证机制对我数据库中的用户运行良好,但@AuthenticationPrincipal 仍然为空。我查阅了几篇互联网帖子,但仍然无效。
网络安全配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider provider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userService);
return provider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/registration").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider());
}
}
消息(实体):
@Entity
@Table(name = "sweater_message")
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String text;
private String tag;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
private User author;
public Message(String text, String tag, User user) {
this.author = user;
this.text = text;
this.tag = tag;
}
public Message() {
}
...getters and setters
用户(实体):
@Entity
@Table(name = "sweater_user")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private boolean active;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(
name = "sweater_user_role",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
)
private Collection<Role> roles;
public User(String username, String password, boolean active, Collection<Role> roles) {
this.username = username;
this.password = password;
this.active = active;
this.roles = roles;
}
public User() {
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return isActive();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles().stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());
...getters and setters
}
用户服务
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
public User save(User user) {
User saveUser = new User(
user.getUsername(),
new BCryptPasswordEncoder().encode(user.getPassword()),
true,
Arrays.asList(new Role("USER")));
return userRepository.save(saveUser);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User findUser = userRepository.findByUsername(username);
if (findUser == null) {
throw new UsernameNotFoundException("There is no user with this username");
}
return new org.springframework.security.core.userdetails.User(
findUser.getUsername(),
findUser.getPassword(),
mapRolesToAuthorities(findUser.getRoles()));
}
public Collection<? extends GrantedAuthority> mapRolesToAuthorities(Collection<Role> roles) {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toSet());
}
}
控制器:
@PostMapping("/main")
public String add(
@AuthenticationPrincipal User user,
@RequestParam String text,
@RequestParam String tag,
Map<String, Object> model
){
...user is null
}
如果您不确定
@AuthenticationPrincipal
应返回什么类型,您可以使用 @AuthenticationPrincipal(errorOnInvalidType=true)
让它抛出异常,如下所示:
@PostMapping("write")
public void write(@AuthenticationPrincipal(errorOnInvalidType=true) String username) {
...
}
并检查控制台输出,您应该看到异常并了解主体类型。例如:
java.lang.ClassCastException: org.springframework.security.oauth2.jwt.Jwt@818e7baa is not assignable to class java.lang.String
尝试将 @AuthenticationPrincipal User user 更改为 @AuthenticationPrincipal UserDetails userDetails,因为 loadUserByUsername 返回 UserDetails
在控制器中使用
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
来查看您的 userdetails 对象是否存储在正确的位置,因为 @AuthenticationPrincipal
是 (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal()
的缩写。
例如:
@GetMapping("/all")
public ResponseEntity<String> test(@AuthenticationPrincipal AuthUserDetails userDetails) {
System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
return ResponseEntity.ok("success");
}
如果输出的内容不是
UserDetails
对象,则意味着您在过滤器类中初始化身份验证时没有正确设置 Principal
。我们以UsernamePasswordAuthenticationToken
为例:
// in filter class
@Component
public class JwtFilter extends OncePerRequestFilter {
private JwtProvider jwtProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Optional<AuthUserDetails> authUserDetailOptional = jwtProvider.resolveToken(request); // extract jwt from request, generate a userdetails object
if (authUserDetailOptional.isPresent()){
AuthUserDetails authUserDetails = authUserDetailOptional.get();
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
authUserDetails, // set your authUserDetails here!!
null,
authUserDetails.getAuthorities()
); // generate authentication object
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
} else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is not valid.");
}
}
我使用OAuthentication-ResourceServer并遇到了同样的问题。 我通过创建自己的 RequestPostProcessor 来修复它:
public static RequestPostProcessor authentication(Authentication authentication) {
return request -> {
SecurityContextHolder.getContext().setAuthentication(authentication);
return request;
};
}