spring-security - 获取异常 spring security 用户帐户被锁定\
问题描述
我们已经在我们的 angular spring boot 项目中实现了 Spring Security。在这里我们得到异常 spring security 用户帐户被锁定
请查看以下代码。
安全配置.java
package com.jwt.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import com.jwt.security.filter.AuthenticationTokenFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration<jwtAuthenticationEntryPoint> extends WebSecurityConfigurerAdapter{
@Autowired private UserDetailsService userDetailsService;
@Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint ;
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder( PasswordEncoder());
}
@Bean
public PasswordEncoder PasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public AuthenticationTokenFilter authenticationTokenFilterBean( ) {
return new AuthenticationTokenFilter();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/registration").permitAll()
.antMatchers("/login").permitAll()
.antMatchers(HttpMethod.OPTIONS ,"/**").permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
httpSecurity.headers().cacheControl();
httpSecurity.headers().httpStrictTransportSecurity().includeSubDomains(true).maxAgeInSeconds(31536000);
}
}
身份验证令牌过滤器 AuthenticationTokenFilter.hjava
package com.jwt.security.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import com.jwt.security.JwtTokenUtil;
public class AuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.header}")
private String tokenHeader;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
String authToken = request.getHeader(this.tokenHeader);
if (authToken != null && authToken.length() > 7) {
authToken = authToken.substring(7);
}
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
boolean isValid = jwtTokenUtil.validateToken(authToken, userDetails);
if (isValid) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
在这里,当我从邮递员运行时,authToken 为空
JwtUtil 的代码如下
package com.jwt.security;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@Component
public class JwtTokenUtil implements Serializable {
static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_AUDIENCE = "audience";
static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String getUsernameFromToken(String authToken) {
String username = null;
try {
final Claims claims = getClaimsFromToken(authToken);
username = claims.getSubject();
} catch (Exception e) {
// TODO Auto-generated catch block
username = null;
}
return username;
}
private Claims getClaimsFromToken(String authToken) {
// TODO Auto-generated method stub
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken).getBody();
} catch (Exception e) {
// TODO Auto-generated catch block
claims = null;
}
return claims;
}
public boolean validateToken(String authToken, UserDetails userDetails) {
// TODO Auto-generated method stub
JwtUser user = (JwtUser) userDetails;
final String username = getUsernameFromToken(authToken);
return (username.equals(user.getUsername()) && !isTokenExpired(authToken));
}
private boolean isTokenExpired(String authToken) {
final Date expiration = getExpirationDateFromToken(authToken);
return expiration.before(new Date());
}
private Date getExpirationDateFromToken(String authToken) {
// TODO Auto-generated method stub
Date expiration = null;
final Claims claims = getClaimsFromToken(authToken);
if (claims != null) {
expiration = claims.getExpiration();
} else {
expiration = null;
}
return expiration;
}
public String generateToken(JwtUser userDetails) {
Map<String,Object> claims = new HashMap<String,Object>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
public String generateToken(Map<String , Object> claims ) {
return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate()).signWith(SignatureAlgorithm.HS512, secret).compact();
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
}
CsrfHeaderFilter 的代码如下
package com.jwt.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;
public class CsrfHeaderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("...CsrfToken.class.getName() :::" + CsrfToken.class.getName());
// CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
// CsrfToken csrfToken = new HttpSessionCsrfTokenRepository().loadToken(request);
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
String token = null;
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
if(csrfToken != null) {
token = csrfToken.getToken();
}
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
filterChain.doFilter(request, response);
}
}
使用的控制器是 AuthenticationController 代码如下
package com.jwt.security.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.jwt.security.JwtTokenUtil;
import com.jwt.security.JwtUser;
import com.jwt.security.domain.User;
import com.jwt.security.domain.UserDTO;
import com.jwt.security.exception.UnauthorizedException;
@RestController
public class AuthenticationController {
@Value("${jwt.header}")
private String tokenHeader;
@Autowired private AuthenticationManager authenticationManager;
@Autowired private JwtTokenUtil jwtTokenUtil;
@PostMapping(value="/login")
public ResponseEntity<UserDTO> login(@RequestBody User user, HttpServletRequest request , HttpServletResponse response) {
try {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
System.out.println("matches ::" + encoder.matches("123", user.getPassword()));
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
final JwtUser userDetails = (JwtUser)authentication.getPrincipal();
SecurityContextHolder.getContext().setAuthentication(authentication);
final String token = jwtTokenUtil.generateToken(userDetails);
response.setHeader("Token", token);
return new ResponseEntity<UserDTO>(new UserDTO(userDetails.getUser(), token) , HttpStatus.OK);
}catch(UnauthorizedException ex) {
ex.printStackTrace();
throw new UnauthorizedException(ex.getMessage());
}
}
}
在从邮递员调用http://localhost:8080/login并传递正确的电子邮件和密码时,我们得到以下异常
org.springframework.security.authentication.LockedException: User account is locked
请指教
解决方案
消息显示“用户帐户已锁定”。这发生在许多失败的身份验证事件之后。该帐户最终会根据实施情况解锁。
Spring Security 中只有两个地方发生:
public void check(UserDetails user) {
if (!user.isAccountNonLocked()) {
throw new LockedException(messages.getMessage(
"AccountStatusUserDetailsChecker.locked", "User account is locked"));
}
if (!user.isEnabled()) {
throw new DisabledException(messages.getMessage(
"AccountStatusUserDetailsChecker.disabled", "User is disabled"));
}
if (!user.isAccountNonExpired()) {
throw new AccountExpiredException(
messages.getMessage("AccountStatusUserDetailsChecker.expired",
"User account has expired"));
}
if (!user.isCredentialsNonExpired()) {
throw new CredentialsExpiredException(messages.getMessage(
"AccountStatusUserDetailsChecker.credentialsExpired",
"User credentials have expired"));
}
}
所以如果你想设置一个断点,那就是你开始的地方。
所有这些都发生在您UserDetailsService
的配置中。
@Autowired private UserDetailsService userDetailsService;
该服务返回一个实现UserDetails
接口的对象
public interface UserDetails {
boolean isAccountNonLocked();
}
如果此方法返回 false,则帐户被锁定。这个名字有点混乱。
由于我们不知道您UserDetailsService
是什么,因此我们无法告诉您这是如何填充的。所以建议只在抛出错误时设置一个断点。
如果您不希望启用帐户锁定功能,则可以使用不同的方法来实现。如果您覆盖UserDetailsService
bean,您总是可以返回从未锁定的用户。
另一种方法是注入你自己的检查器
DaoAuthenticationProvider daoProvider = ....
daoProvider.setPreAuthenticationChecks(toCheck -> {});
还有一个PostAuthenticationChecks
对象可以查看您的密码是否已过期。
推荐阅读
- vba - VBA 中可接受的参数
- css - Sass 全球可访问的 Mixin
- python - 如何将按 x、y、z 坐标排序的 pandas 数据框转换为 numpy 数组列表?
- rust - 如何在 Rust 中复制一个值?
- kubernetes - Prometheus 如何知道 Pod 何时崩溃?
- python - AttributeError:“dict”对象没有属性“__name__”
- sqlite - SQLite - 电话号码输出
- javascript - 禁用提交按钮,直到填写字段
- r - 通过 knitr + pandoc 将 R 降价为 html:错误 137
- css - Qt 可以创建类似 Blender 的 GUI 吗?