添加短信登录接口,未测试。文档未编写
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
##本项目的参数配置说明
|
||||
本项目说明中的配置参数是boot-starter-security-browser和boot-starter-security-app
|
||||
项目的交集
|
||||
- 用户登录接口url`/user/login`
|
||||
- 图片验证码获取接口url`/code/image`
|
||||
- 用户登录接口url`/user/login`
|
||||
- 图片验证码获取接口url`/code/image` ,在进行图片验证码校验时传递参数为`imageCode`
|
||||
- `ms.security.code.image.width` 图片验证码宽度,默认值是`67` 可以在请求中使用`width`参数设置
|
||||
- `ms.security.code.image.height`图片验证码高度,默认值是`23` 可以在请求中使用`height`参数设置
|
||||
- `ms.security.code.image.length`图片验证码的code长度,默认是`4` 不能在请求中设置
|
||||
@@ -13,10 +13,12 @@
|
||||
不能在请求中设置
|
||||
- 本着增量开发的原则,可以实现`imageCodeUtil`生成验证码的方法,并定义名称为`imageCodeUtil`的bean便可替换原有的图片验证码
|
||||
的生成逻辑
|
||||
- 短信验证码获取接口url`/code/sms`
|
||||
- 短信验证码获取接口url`/code/sms` 传递参数为`phone`
|
||||
- `ms.security.code.sms.length` 短信验证码code的长度,默认值是`6`,不能在请求中设置
|
||||
- `ms.security.code.sms.expireIn` 短信验证码的默认有效时间是`60`秒,不能在请求中设置
|
||||
- `ms.security.code.sms.url`短信验证码进行耀眼的地址,默认值是`/user/login`,可以是`/user/*`这种格式,多个适用,号分割,不
|
||||
能在请求中设置
|
||||
- 同样的,可以实现`validateCode`生成验证码的方法,并定义名称为`smsCodeUtil`的bean便可替换原有的短信验证码的生成逻辑
|
||||
|
||||
- 短信登录的接口为`/authentication/mobile`,传递参数为`smsCode`和`mobile`
|
||||
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.micro.fast.security.core.authentication.mobile;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 身份认证过滤器
|
||||
* @author lsy
|
||||
*/
|
||||
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
public static final String MICRO_FAST_MOBILE_KEY = "mobile";
|
||||
public static final String AUTHENTICATION_MOBILE_URL = "/authentication/mobile";
|
||||
|
||||
private String mobileParameter = MICRO_FAST_MOBILE_KEY;
|
||||
|
||||
/**
|
||||
* 是不是只处理post请求
|
||||
*/
|
||||
private boolean postOnly = true;
|
||||
|
||||
/**
|
||||
* 对/authentication/mobile路径的post请求进行身份认证
|
||||
*/
|
||||
public SmsCodeAuthenticationFilter() {
|
||||
super(new AntPathRequestMatcher(AUTHENTICATION_MOBILE_URL, "POST"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 主要的身份认证方法
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
||||
if (this.postOnly && !request.getMethod().equals("POST")) {
|
||||
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
|
||||
} else {
|
||||
//获取请求中的mobile
|
||||
String mobile = this.obtainMobile(request);
|
||||
if (mobile == null) {
|
||||
mobile = "";
|
||||
}
|
||||
mobile = mobile.trim();
|
||||
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
|
||||
this.setDetails(request, authRequest);
|
||||
//调用AuthenticationManager中的方法
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机号的方法
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
protected String obtainMobile(HttpServletRequest request) {
|
||||
return request.getParameter(this.mobileParameter);
|
||||
}
|
||||
|
||||
protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
|
||||
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||
}
|
||||
|
||||
public void setMobileParameter(String mobileParameter) {
|
||||
Assert.hasText(mobileParameter, "Username parameter must not be empty or null");
|
||||
this.mobileParameter = mobileParameter;
|
||||
}
|
||||
|
||||
public void setPostOnly(boolean postOnly) {
|
||||
this.postOnly = postOnly;
|
||||
}
|
||||
|
||||
public final String getMobileParameter() {
|
||||
return this.mobileParameter;
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.micro.fast.security.core.authentication.mobile;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
/**
|
||||
* 进行短信登录的身份认证提供类
|
||||
* @author lsy
|
||||
*/
|
||||
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
/**
|
||||
* 返回经过验证的Authentication
|
||||
* @param authentication
|
||||
* @return
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
SmsCodeAuthenticationToken smsCodeAuthenticationToken = (SmsCodeAuthenticationToken) authentication;
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername((String) smsCodeAuthenticationToken.getPrincipal());
|
||||
if (userDetails == null) {
|
||||
throw new InternalAuthenticationServiceException("无法获取用户的信息");
|
||||
}
|
||||
SmsCodeAuthenticationToken resultSmsCodeAuthenticationToken = new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
|
||||
resultSmsCodeAuthenticationToken.setDetails(smsCodeAuthenticationToken.getDetails());
|
||||
return resultSmsCodeAuthenticationToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个类是不是支持传入的token的校验
|
||||
* @param authenticate
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> authenticate) {
|
||||
return SmsCodeAuthenticationToken.class.isAssignableFrom(authenticate);
|
||||
}
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.micro.fast.security.core.authentication.mobile;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 短信验证登陆的身份令牌类
|
||||
* @author lsy
|
||||
*/
|
||||
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Object principal;
|
||||
|
||||
/**
|
||||
* 在没有进行身份认证的时候存储手机号
|
||||
* @param mobile 手机号
|
||||
*/
|
||||
public SmsCodeAuthenticationToken(String mobile) {
|
||||
super((Collection)null);
|
||||
this.principal = mobile;
|
||||
this.setAuthenticated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param principal
|
||||
* @param authorities
|
||||
*/
|
||||
public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
super.setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
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");
|
||||
} else {
|
||||
super.setAuthenticated(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eraseCredentials() {
|
||||
super.eraseCredentials();
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package com.micro.fast.security.core.validate.code.constant;
|
||||
|
||||
/**
|
||||
* 验证码校验常量类
|
||||
* @author lsy
|
||||
*/
|
||||
public class ValidateCodeConst {
|
||||
/**
|
||||
* 从session中获取验证码校验对象的key
|
||||
*/
|
||||
public static final String SESSION_KEY_IMAGE_CODE = "SESSION_KEY_IMAGE_CODE";
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package com.micro.fast.security.core.validate.code.filter;
|
||||
|
||||
import com.micro.fast.security.core.authentication.mobile.SmsCodeAuthenticationFilter;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.core.validate.code.exception.ValidateCodeException;
|
||||
import com.micro.fast.security.core.validate.code.pojo.ValidateCode;
|
||||
import com.micro.fast.security.core.validate.code.processor.ValidateCodeProcessor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
|
||||
import org.springframework.social.connect.web.SessionStrategy;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
public class SmsValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
|
||||
private AuthenticationFailureHandler authenticationFailureHandler;
|
||||
/**
|
||||
* session操作工具类
|
||||
*/
|
||||
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
|
||||
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
private Set<String> filterUrl = new HashSet<String>();
|
||||
|
||||
private static final String REQUEST_METHOD = "post";
|
||||
|
||||
/**
|
||||
* 初始化filter url
|
||||
* @throws ServletException
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws ServletException {
|
||||
super.afterPropertiesSet();
|
||||
String url = securityProperties.getCode().getSms().getUrl();
|
||||
String[] urls = url.split(",");
|
||||
for (String s : urls) {
|
||||
filterUrl.add(s);
|
||||
}
|
||||
filterUrl.add(SmsCodeAuthenticationFilter.AUTHENTICATION_MOBILE_URL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest httpServletRequest
|
||||
, HttpServletResponse httpServletResponse
|
||||
, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
//判断是否需要验证码校验
|
||||
boolean filter = false;
|
||||
AntPathMatcher antPathMatcher = new AntPathMatcher();
|
||||
Iterator<String> iterator = filterUrl.iterator();
|
||||
while (iterator.hasNext()){
|
||||
if (antPathMatcher.match(iterator.next(),httpServletRequest.getRequestURI())){
|
||||
filter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//在登录post请求的时候对验证码进行校验
|
||||
if (filter && StringUtils.endsWithIgnoreCase(httpServletRequest.getMethod(),REQUEST_METHOD)){
|
||||
try{
|
||||
//校验验证码是否正确
|
||||
validate(new ServletWebRequest(httpServletRequest));
|
||||
}catch(ValidateCodeException e){
|
||||
//进行身份认证失败后的处理
|
||||
authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
|
||||
//身份认证失败的时候直接return返回给页面信息,不进行后续的
|
||||
return;
|
||||
}
|
||||
}
|
||||
//执行后续的过滤器
|
||||
filterChain.doFilter(httpServletRequest,httpServletResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码校验
|
||||
* @param servletWebRequest
|
||||
*/
|
||||
private void validate(ServletWebRequest servletWebRequest) {
|
||||
ValidateCode validateCode = (ValidateCode)(sessionStrategy.getAttribute(servletWebRequest, ValidateCodeProcessor.SESSION_KEY_PREFIX+"SMS"));
|
||||
String code = servletWebRequest.getParameter("smsCode");
|
||||
//session和用户传入的验证码不能为空
|
||||
if (validateCode==null || StringUtils.isBlank(code)){
|
||||
throw new ValidateCodeException("未生成验证码对象或用户传入的验证码为空");
|
||||
}
|
||||
if (validateCode.getCode()==null
|
||||
|| !validateCode.getCode().equals(code)){
|
||||
throw new ValidateCodeException("验证码校验失败");
|
||||
}
|
||||
if (validateCode.isExpire()){
|
||||
throw new ValidateCodeException("验证码已过期");
|
||||
}
|
||||
//一次校验完成之后删除验证码
|
||||
sessionStrategy.removeAttribute(servletWebRequest,ValidateCodeProcessor.SESSION_KEY_PREFIX+"SMS");
|
||||
}
|
||||
|
||||
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
|
||||
return authenticationFailureHandler;
|
||||
}
|
||||
|
||||
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
|
||||
this.authenticationFailureHandler = authenticationFailureHandler;
|
||||
}
|
||||
|
||||
public Set<String> getFilterUrl() {
|
||||
return filterUrl;
|
||||
}
|
||||
|
||||
public void setFilterUrl(Set<String> filterUrl) {
|
||||
this.filterUrl = filterUrl;
|
||||
}
|
||||
|
||||
public SecurityProperties getSecurityProperties() {
|
||||
return securityProperties;
|
||||
}
|
||||
|
||||
public void setSecurityProperties(SecurityProperties securityProperties) {
|
||||
this.securityProperties = securityProperties;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
package com.micro.fast.security.core.validate.code.filter;
|
||||
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.core.validate.code.constant.ValidateCodeConst;
|
||||
import com.micro.fast.security.core.validate.code.exception.ValidateCodeException;
|
||||
import com.micro.fast.security.core.validate.code.pojo.ValidateCode;
|
||||
import com.micro.fast.security.core.validate.code.processor.ValidateCodeProcessor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
@@ -93,7 +93,7 @@ public class ValidateCodeFilter extends OncePerRequestFilter implements Initial
|
||||
* @param servletWebRequest
|
||||
*/
|
||||
private void validate(ServletWebRequest servletWebRequest) {
|
||||
ValidateCode validateCode = (ValidateCode)(sessionStrategy.getAttribute(servletWebRequest, ValidateCodeConst.SESSION_KEY_IMAGE_CODE));
|
||||
ValidateCode validateCode = (ValidateCode)(sessionStrategy.getAttribute(servletWebRequest, ValidateCodeProcessor.SESSION_KEY_PREFIX+"IMAGE"));
|
||||
String code = servletWebRequest.getParameter("imageCode");
|
||||
//session和用户传入的验证码不能为空
|
||||
if (validateCode==null || StringUtils.isBlank(code)){
|
||||
@@ -107,7 +107,7 @@ public class ValidateCodeFilter extends OncePerRequestFilter implements Initial
|
||||
throw new ValidateCodeException("验证码已过期");
|
||||
}
|
||||
//一次校验完成之后删除验证码
|
||||
sessionStrategy.removeAttribute(servletWebRequest,ValidateCodeConst.SESSION_KEY_IMAGE_CODE);
|
||||
sessionStrategy.removeAttribute(servletWebRequest,ValidateCodeProcessor.SESSION_KEY_PREFIX+"IMAGE");
|
||||
}
|
||||
|
||||
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
|
||||
|
||||
Reference in New Issue
Block a user