英文:
Spring Security - 401 Unauthorized access
问题
我已创建一个向后端发送数据并将其持久化到数据库中的表单。
只要我在antMatcher上使用.permitAll(),这个方法就可以正常工作,但是当我尝试对其进行安全保护,只允许管理员调用(数据库中的管理员角色为ROLE_ADMIN)时,它返回一个没有消息的401未经授权访问错误。
我尝试过:
- .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 {
    // ... 这里是您的配置代码 ...
}
我的UserDetailsServiceImpl类:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    // ... 这里是您的服务实现代码 ...
}
我的JwtAuthenticationEntryPoint类:
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    // ... 这里是您的认证入口点代码 ...
}
我的JwtAuthenticationFilter类:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    // ... 这里是您的JWT身份验证过滤器代码 ...
}
JWT令牌的有效性已经被正确检查,这不是问题的关键。任何帮助将不胜感激。
添加了UserDetailsImpl的实现:
public class UserDetailsImpl implements UserDetails {
    // ... 这里是您的UserDetailsImpl实现代码 ...
}
登录部分的AuthenticationController:
@RestController
@RequestMapping("/api/auth")
public class AuthenticationController {
    // ... 这里是您的认证控制器的代码 ...
}
如果您需要更多帮助,请随时提问。
英文:
I've created a form that sends the data to my backend, which persists it into the database 
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")
 
My request looks like this (posting for the headers):
My SecurityConfig class:
@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);
}
}
My UserDetailsServiceImpl class:
@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);
}
}
My JwtAuthenticationEntryPoint class:
@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());
}
}
My 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;
}
}
The JWT Token's validity is properly checked. That's not the issue at hand.
Any help is appreciated.
EDIT:
Added implementation of 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);
}
Added this to check if the authorities are present after UserDetailsImpl.create(user) call:

Output:
Login part of the AuthenticationController:
答案1
得分: 1
我看到您没有更新SecurityContextHolder。无法将其放在评论中,所以我在这里写了出来。
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
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.
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest))
SecurityContextHolder.getContext().setAuthentication(authentication); //this seems missing
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。







评论