春季安全 – 401 未经授权访问

huangapple go评论117阅读模式
英文:

Spring Security - 401 Unauthorized access

问题

我已创建一个向后端发送数据并将其持久化到数据库中的表单。

只要我在antMatcher上使用.permitAll(),这个方法就可以正常工作,但是当我尝试对其进行安全保护,只允许管理员调用(数据库中的管理员角色为ROLE_ADMIN)时,它返回一个没有消息的401未经授权访问错误。

我尝试过:

  • .hasRole("ADMIN")
  • .hasRole("ROLE_ADMIN")
  • .hasAuthority("ADMIN")
  • .hasAuthority("ROLE_ADMIN")

这些都不起作用。

我的请求如下(用于头部):

春季安全 – 401 未经授权访问

我的SecurityConfig类:

  1. @Configuration
  2. @EnableWebSecurity
  3. @EnableGlobalMethodSecurity(
  4. securedEnabled = true,
  5. jsr250Enabled = true,
  6. prePostEnabled = true
  7. )
  8. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  9. // ... 这里是您的配置代码 ...
  10. }

我的UserDetailsServiceImpl类:

  1. @Service
  2. public class UserDetailsServiceImpl implements UserDetailsService {
  3. // ... 这里是您的服务实现代码 ...
  4. }

我的JwtAuthenticationEntryPoint类:

  1. @Component
  2. public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
  3. // ... 这里是您的认证入口点代码 ...
  4. }

我的JwtAuthenticationFilter类:

  1. public class JwtAuthenticationFilter extends OncePerRequestFilter {
  2. // ... 这里是您的JWT身份验证过滤器代码 ...
  3. }

JWT令牌的有效性已经被正确检查,这不是问题的关键。任何帮助将不胜感激。

添加了UserDetailsImpl的实现:

  1. public class UserDetailsImpl implements UserDetails {
  2. // ... 这里是您的UserDetailsImpl实现代码 ...
  3. }

登录部分的AuthenticationController:

  1. @RestController
  2. @RequestMapping("/api/auth")
  3. public class AuthenticationController {
  4. // ... 这里是您的认证控制器的代码 ...
  5. }

如果您需要更多帮助,请随时提问。

英文:

I've created a form that sends the data to my backend, which persists it into the database 春季安全 – 401 未经授权访问

This works well as long as I have .permitAll() on my antMatcher, but when I try to secure it so that only admins can make that call (admin role in the DB is ROLE_ADMIN), it returns a 401 Unauthorized Access with no message. I've tried

  • .hasRole("ADMIN")
  • .hasRole("ROLE_ADMIN")
  • .hasAuthority("ADMIN")
  • .hasAuthority("ROLE_ADMIN")

None of them works.
春季安全 – 401 未经授权访问

春季安全 – 401 未经授权访问

My request looks like this (posting for the headers):

春季安全 – 401 未经授权访问

My SecurityConfig class:

  1. @Configuration
  2. @EnableWebSecurity
  3. @EnableGlobalMethodSecurity(
  4. securedEnabled = true,
  5. jsr250Enabled = true,
  6. prePostEnabled = true
  7. )
  8. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  9. @Autowired
  10. UserDetailsServiceImpl userDetailsService;
  11. @Autowired
  12. private JwtAuthenticationEntryPoint unauthorizedHandler;
  13. @Bean
  14. public JwtAuthenticationFilter jwtAuthenticationFilter() {
  15. return new JwtAuthenticationFilter();
  16. }
  17. @Override
  18. public void configure(AuthenticationManagerBuilder auth) throws Exception {
  19. auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
  20. }
  21. @Bean(BeanIds.AUTHENTICATION_MANAGER)
  22. @Override
  23. public AuthenticationManager authenticationManagerBean() throws Exception {
  24. return super.authenticationManagerBean();
  25. }
  26. @Bean
  27. public PasswordEncoder passwordEncoder() {
  28. return new BCryptPasswordEncoder();
  29. }
  30. @Override
  31. protected void configure(HttpSecurity http) throws Exception {
  32. http
  33. .cors()
  34. .and()
  35. .csrf()
  36. .disable()
  37. .exceptionHandling()
  38. .authenticationEntryPoint(unauthorizedHandler)
  39. .and()
  40. .sessionManagement()
  41. .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  42. .and()
  43. .authorizeRequests()
  44. .antMatchers("/",
  45. "/favicon.ico",
  46. "/**/*.png",
  47. "/**/*.gif",
  48. "/**/*.svg",
  49. "/**/*.jpg",
  50. "/**/*.html",
  51. "/**/*.css",
  52. "/**/*.js")
  53. .permitAll()
  54. .antMatchers("/api/auth/**")
  55. .permitAll()
  56. .antMatchers("/api/book/**")
  57. .permitAll()
  58. .antMatchers("/api/author/**")
  59. // .permitAll()
  60. .hasAnyRole("ROLE_ADMIN", "ADMIN", "ROLE_USER", "USER", "ROLE_ROLE_ADMIN",
  61. "ROLE_ROLE_USER")
  62. .anyRequest()
  63. .authenticated();
  64. http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
  65. }
  66. }

My UserDetailsServiceImpl class:

  1. @Service
  2. public class UserDetailsServiceImpl implements UserDetailsService {
  3. @Autowired
  4. UserRepository userRepository;
  5. @Override
  6. @Transactional
  7. public UserDetails loadUserByUsername(String email)
  8. throws UsernameNotFoundException {
  9. User user = userRepository.findByEmail(email);
  10. return UserDetailsImpl.create(user);
  11. }
  12. @Transactional
  13. public UserDetails loadUserById(Integer id) {
  14. User user = userRepository.findById(id).orElseThrow(
  15. () -> new UsernameNotFoundException("User not found with id: " + id)
  16. );
  17. return UserDetailsImpl.create(user);
  18. }
  19. }

My JwtAuthenticationEntryPoint class:

  1. @Component
  2. public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
  3. private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
  4. @Override
  5. public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
  6. AuthenticationException e) throws IOException, ServletException {
  7. logger.error("Unauthorized access. Message:", e.getMessage());
  8. httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
  9. }
  10. }

My JwtAuthenticationFilter:

  1. public class JwtAuthenticationFilter extends OncePerRequestFilter {
  2. @Autowired
  3. private JwtTokenProvider tokenProvider;
  4. @Autowired
  5. private UserDetailsServiceImpl userDetailsService;
  6. private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
  7. @Override
  8. protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse
  9. httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
  10. try {
  11. String jwt = getJwtFromRequest(httpServletRequest);
  12. if(StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
  13. Integer userId = tokenProvider.getUserIdFromJWT(jwt);
  14. UserDetails userDetails = userDetailsService.loadUserById(userId);
  15. UsernamePasswordAuthenticationToken authentication = new
  16. UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
  17. authentication.setDetails(new
  18. WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
  19. }
  20. } catch (Exception e) {
  21. logger.error("Could not set user authentication in security context", e);
  22. }
  23. filterChain.doFilter(httpServletRequest, httpServletResponse);
  24. }
  25. private String getJwtFromRequest(HttpServletRequest request) {
  26. String bearerToken = request.getHeader("Authorization");
  27. if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
  28. return bearerToken.substring(7, bearerToken.length());
  29. }
  30. return null;
  31. }
  32. }

The JWT Token's validity is properly checked. That's not the issue at hand.
Any help is appreciated.

EDIT:
Added implementation of UserDetailsImpl:

  1. public class UserDetailsImpl implements UserDetails {
  2. private Integer id;
  3. @JsonIgnore
  4. private String email;
  5. private String name;
  6. @JsonIgnore
  7. private String password;
  8. private boolean isAdmin;
  9. private Collection<? extends GrantedAuthority> authorities;
  10. public UserDetailsImpl(Integer id, String email, String name, String
  11. password, boolean isAdmin, Collection<? extends GrantedAuthority>
  12. authorities) {
  13. this.id = id;
  14. this.name = name;
  15. this.email = email;
  16. this.password = password;
  17. this.authorities = authorities;
  18. this.isAdmin = isAdmin;
  19. }
  20. public static UserDetailsImpl create(User user) {
  21. List<GrantedAuthority> authorities = user.getRoles().stream().map(role ->
  22. new SimpleGrantedAuthority(role.getName().name())
  23. ).collect(Collectors.toList());
  24. boolean isAdmin = false;
  25. for(Role role: user.getRoles()) {
  26. if(RoleName.ROLE_ADMIN.equals(role.getName())) {
  27. isAdmin = true;
  28. }
  29. }
  30. return new UserDetailsImpl(
  31. user.getId(),
  32. user.getEmail(),
  33. user.getName(),
  34. user.getPassword(),
  35. isAdmin,
  36. authorities
  37. );
  38. }
  39. public Integer getId() {
  40. return id;
  41. }
  42. public String getName() {
  43. return name;
  44. }
  45. @Override
  46. public String getUsername() {
  47. return email;
  48. }
  49. @Override
  50. public String getPassword() {
  51. return password;
  52. }
  53. public boolean isAdmin() {
  54. return isAdmin;
  55. }
  56. @Override
  57. public Collection<? extends GrantedAuthority> getAuthorities() {
  58. return authorities;
  59. }
  60. @Override
  61. public boolean isAccountNonExpired() {
  62. return true;
  63. }
  64. @Override
  65. public boolean isAccountNonLocked() {
  66. return true;
  67. }
  68. @Override
  69. public boolean isCredentialsNonExpired() {
  70. return true;
  71. }
  72. @Override
  73. public boolean isEnabled() {
  74. return true;
  75. }
  76. @Override
  77. public boolean equals(Object o) {
  78. if (this == o) return true;
  79. if (o == null || getClass() != o.getClass()) return false;
  80. UserDetailsImpl that = (UserDetailsImpl) o;
  81. return Objects.equals(id, that.id);
  82. }
  83. @Override
  84. public int hashCode() {
  85. return Objects.hash(id);
  86. }

Added this to check if the authorities are present after UserDetailsImpl.create(user) call:
春季安全 – 401 未经授权访问

Output:

春季安全 – 401 未经授权访问

Login part of the AuthenticationController:

春季安全 – 401 未经授权访问

答案1

得分: 1

我看到您没有更新SecurityContextHolder。无法将其放在评论中,所以我在这里写了出来。

  1. authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
  2. SecurityContextHolder.getContext().setAuthentication(authentication); //这部分似乎丢失了
英文:

I see that you are not updating the SecurityContextHolder. Unable to put it in a comment, so I wrote it here.

  1. authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest))
  2. SecurityContextHolder.getContext().setAuthentication(authentication); //this seems missing

huangapple
  • 本文由 发表于 2020年10月19日 21:00:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/64427881.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定