Spring Security - 401未经授权的访问

Posted

技术标签:

【中文标题】Spring Security - 401未经授权的访问【英文标题】:Spring Security - 401 Unauthorized access 【发布时间】:2021-02-02 06:16:41 【问题描述】:

我创建了一个表单,将数据发送到我的后端,并将其保存到数据库

只要我的 antMatcher 上有 .permitAll() 就可以很好地工作,但是当我尝试保护它以便只有管理员可以进行调用时(数据库中的管理员角色是 ROLE_ADMIN),它会返回 401 Unauthorized Access没有消息。我试过了

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

它们都不起作用。

我的请求看起来像这样(发布标题):

我的 SecurityConfig 类:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
    securedEnabled = true,
    jsr250Enabled = true,
    prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter 

@Autowired
UserDetailsServiceImpl userDetailsService;

@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() 
    return new JwtAuthenticationFilter();


@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception 
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());


@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception 
    return super.authenticationManagerBean();


@Bean
public PasswordEncoder passwordEncoder() 
    return new BCryptPasswordEncoder();


@Override
protected void configure(HttpSecurity http) throws Exception 
    http
            .cors()
                .and()
            .csrf()
                .disable()
            .exceptionHandling()
                .authenticationEntryPoint(unauthorizedHandler)
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers("/",
                    "/favicon.ico",
                    "/**/*.png",
                    "/**/*.gif",
                    "/**/*.svg",
                    "/**/*.jpg",
                    "/**/*.html",
                    "/**/*.css",
                    "/**/*.js")
                    .permitAll()
                .antMatchers("/api/auth/**")
                    .permitAll()
                .antMatchers("/api/book/**")
                    .permitAll()
                .antMatchers("/api/author/**")
//                        .permitAll()
                    .hasAnyRole("ROLE_ADMIN", "ADMIN", "ROLE_USER", "USER", "ROLE_ROLE_ADMIN", 
"ROLE_ROLE_USER")
            .anyRequest()
            .authenticated();

    http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);


我的 UserDetailsS​​erviceImpl 类:

@Service
public class UserDetailsServiceImpl implements UserDetailsService 

@Autowired
UserRepository userRepository;

@Override
@Transactional
public UserDetails loadUserByUsername(String email)
        throws UsernameNotFoundException 
    User user = userRepository.findByEmail(email);

    return UserDetailsImpl.create(user);


@Transactional
public UserDetails loadUserById(Integer id) 
    User user = userRepository.findById(id).orElseThrow(
            () -> new UsernameNotFoundException("User not found with id: " + id)
    );

    return UserDetailsImpl.create(user);


我的 JwtAuthenticationEntryPoint 类:

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint 

private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);

@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, 
AuthenticationException e) throws IOException, ServletException 
    logger.error("Unauthorized access. Message:", e.getMessage());
    httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());


我的 JwtAuthenticationFilter:

public class JwtAuthenticationFilter extends OncePerRequestFilter 

@Autowired
private JwtTokenProvider tokenProvider;

@Autowired
private UserDetailsServiceImpl userDetailsService;

private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);


@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse 
httpServletResponse, FilterChain filterChain) throws ServletException, IOException 
    try 
        String jwt = getJwtFromRequest(httpServletRequest);

        if(StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) 
            Integer userId = tokenProvider.getUserIdFromJWT(jwt);

            UserDetails userDetails = userDetailsService.loadUserById(userId);
            UsernamePasswordAuthenticationToken authentication = new 
UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

            authentication.setDetails(new 
WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
        
     catch (Exception e) 
        logger.error("Could not set user authentication in security context", e);
    

    filterChain.doFilter(httpServletRequest, httpServletResponse);


private String getJwtFromRequest(HttpServletRequest request) 
    String bearerToken = request.getHeader("Authorization");
    if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) 
        return bearerToken.substring(7, bearerToken.length());
    
    return null;


正确检查 JWT Token 的有效性。这不是手头的问题。 任何帮助表示赞赏。

编辑: 添加了 UserDetailsImpl 的实现:

public class UserDetailsImpl implements UserDetails 
private Integer id;

@JsonIgnore
private String email;

private String name;

@JsonIgnore
private String password;

private boolean isAdmin;

private Collection<? extends GrantedAuthority> authorities;

public UserDetailsImpl(Integer id, String email, String name, String 
password, boolean isAdmin, Collection<? extends GrantedAuthority> 
authorities) 
    this.id = id;
    this.name = name;
    this.email = email;
    this.password = password;
    this.authorities = authorities;
    this.isAdmin = isAdmin;


public static UserDetailsImpl create(User user) 
    List<GrantedAuthority> authorities = user.getRoles().stream().map(role ->
            new SimpleGrantedAuthority(role.getName().name())
    ).collect(Collectors.toList());

    boolean isAdmin = false;

    for(Role role:  user.getRoles()) 
        if(RoleName.ROLE_ADMIN.equals(role.getName())) 
            isAdmin = true;
        
    

    return new UserDetailsImpl(
            user.getId(),
            user.getEmail(),
            user.getName(),
            user.getPassword(),
            isAdmin,
            authorities
    );


public Integer getId() 
    return id;


public String getName() 
    return name;


@Override
public String getUsername() 
    return email;


@Override
public String getPassword() 
    return password;


public boolean isAdmin() 
    return isAdmin;


@Override
public Collection<? extends GrantedAuthority> getAuthorities() 
    return authorities;


@Override
public boolean isAccountNonExpired() 
    return true;


@Override
public boolean isAccountNonLocked() 
    return true;


@Override
public boolean isCredentialsNonExpired() 
    return true;


@Override
public boolean isEnabled() 
    return true;


@Override
public boolean equals(Object o) 
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    UserDetailsImpl that = (UserDetailsImpl) o;
    return Objects.equals(id, that.id);


@Override
public int hashCode() 

    return Objects.hash(id);

添加此以检查 UserDetailsImpl.create(user) 调用后是否存在权限:

输出:

AuthenticationController 的登录部分:

【问题讨论】:

您的用户有管理员角色吗? 您能分享一下您的 UserDetailsImpl.create() 实现吗?也许你当时没有授予适当的角色和权限 你能告诉我们userDetails.getAuthorities()的输出是什么吗? @tashkhisi 据我所知,是的,它应该有。 @Youri 我在帖子末尾添加了 .create(user) 之后 .getauthorities 的实现和输出 【参考方案1】:

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

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

【讨论】:

哦,JwtAuthenticationFilter 类中缺少它。我看看能不能解决问题。

以上是关于Spring Security - 401未经授权的访问的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot / Spring Security中的错误401未经授权

Spring Security OAuth2 SSO 未经授权的 401 错误

在 grails 应用程序中调用 spring security rest 插件登录时出现 401 未经授权的错误

Spring Security:在 401 的情况下重定向到登录页面

Spring Boot 401 未经授权的邮递员

转到Spring Boot 1.5.1和OAuth2 + JWT令牌 - 错误401未经授权