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);
我的 UserDetailsServiceImpl 类:
@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 未经授权的错误