在对security进行用户名密码登录之后还要满足手机验证码登录,由于是初次使用security进行手机验证,并且由于前端用的json对象进行传参,导致在验证时出现了一些问题。由于在网上也没查到使用json对象进行手机验证的操作,于是自己便根据用户名密码登录的操作进行了一些改写。
继承AbstractAuthenticationProcessingFilter
private String mobileParameter = "mobile";
private String smsCodeParameter = "smsCode";
private boolean postOnly = true;
protected SmsCodeAuthenticationFilter() {
// 请求接口的url
super(new AntPathRequestMatcher("/user/phone", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
if(httpServletRequest.getContentType() != null && (httpServletRequest.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)
|| httpServletRequest.getContentType().equals(MediaType.APPLICATION_JSON_VALUE))) {
ObjectMapper mapper = new ObjectMapper();
SmsCodeAuthenticationToken authRequest = null;
try (InputStream is = httpServletRequest.getInputStream() ) {
Map<String, String> authenticationBean = mapper.readValue(is, Map.class);
authRequest = new SmsCodeAuthenticationToken(authenticationBean.get("mobile"), authenticationBean.get("smsCode"));
} catch (IOException e) {
e.printStackTrace();
authRequest = new SmsCodeAuthenticationToken("", "");
}finally {
setDetails(httpServletRequest,authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
} else {
if (postOnly && !httpServletRequest.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + httpServletRequest.getMethod());
} else {
//根据请求参数名,获取请求value
String mobile = obtainMobile(httpServletRequest);
String smsCode = obtainSmsCode(httpServletRequest);
logger.info("mobile:{}", mobile);
if (mobile == null) {
mobile = "";
}
mobile = mobile.trim();
// 生成对应的AuthenticationToken
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile, smsCode);
setDetails(httpServletRequest, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
}
@Nullable
protected String obtainMobile(HttpServletRequest request) {
return request.getParameter(mobileParameter);
}
@Nullable
protected String obtainSmsCode(HttpServletRequest request) {
return request.getParameter(smsCodeParameter);
}
protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
实现 AuthenticationProvider
@Autowired
private SmsUserDetailsService smsUserDetailsService;
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
/**
* 身份逻辑验证
* @param authentication
* @return
* @throws AuthenticationException*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
String mobile = (String) authenticationToken.getPrincipal();
String smsCode = (String) authenticationToken.getCredentials();
logger.info(mobile + ":" + smsCode);
checkSmsCode(mobile,smsCode);
UserDetails user = smsUserDetailsService.loadUserByUsername(mobile);
if (user == null) {
throw new BadCredentialsException("无法获取用户信息");
}
SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
private void checkSmsCode(String mobile , String inputCode) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (redisTemplate == null) {
throw new BadCredentialsException("未检测到申请验证码");
}
String smsCode = (String) redisTemplate.opsForValue().get(mobile);
System.out.println(smsCode);
if(smsCode == null) {
throw new BadCredentialsException("未检测到申请验证码");
}
if(smsCode == null ){
throw new BadCredentialsException("验证码已失效");
}
if(!smsCode.equals(inputCode)) {
throw new BadCredentialsException("验证码错误");
}
}
@Override
public boolean supports(Class<?> authentication) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
public SmsUserDetailsService getSmsUserDetailsService() {
return smsUserDetailsService;
}
public void setSmsUserDetailsService(SmsUserDetailsService smsUserDetailsService) {
this.smsUserDetailsService = smsUserDetailsService;
}
继承AbstractAuthenticationToken
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
private Object credentials;
public SmsCodeAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public SmsCodeAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
配置securityConfig
http.addFilterAt(smsCodeAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).authenticationProvider(smsCodeAuthenticationProvider);
http.addFilterAt(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).authenticationProvider(userAuthenticationProvider);
/******************************/
@Bean
MyAuthenticationFilter myAuthenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
@Bean
SmsCodeAuthenticationFilter smsCodeAuthenticationFilter() throws Exception {
SmsCodeAuthenticationFilter filter = new SmsCodeAuthenticationFilter();
filter.setAuthenticationSuccessHandler(smsAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(smsAuthenticationFailureHandler);
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}