完成短信验证码验证,以及短信登录开发,抽离出公共的常量

This commit is contained in:
lsy
2017-12-17 17:12:41 +08:00
parent fa9d9550a3
commit 62bee09699
21 changed files with 373 additions and 343 deletions
@@ -37,7 +37,7 @@ public class PropertiesUtil {
}
public void loadProperties(String projectPath){
//2.将mybatisStart.properties配置文件加载到输入流中
File file = new File( projectPath+"/src/main/resources/"+PROPERTIES_PATH);
File file = new File( projectPath+ "/src/main/resources/"+PROPERTIES_PATH);
if (file.exists()){
InputStream inputStream = null;
try {
@@ -54,6 +54,6 @@ public class PropertiesUtil {
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
@@ -1,5 +1,7 @@
package com.micro.fast.security.browser.config;
import com.micro.fast.security.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig;
import com.micro.fast.security.core.master.SecurityConstants;
import com.micro.fast.security.core.master.SecurityProperties;
import com.micro.fast.security.core.validate.code.filter.ValidateCodeFilter;
import org.springframework.beans.factory.annotation.Autowired;
@@ -37,104 +39,117 @@ import javax.sql.DataSource;
*/
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 注入身份认证框架配置参数对象
*/
@Autowired
private SecurityProperties securityProperties;
/**
* 注入身份认证框架配置参数对象
*/
@Autowired
private SecurityProperties securityProperties;
/**
* 注入身份认证成功后续处理对象
*/
@Autowired
private AuthenticationSuccessHandler msAuthenticationSuccessHandler;
/**
* 注入身份认证成功后续处理对象
*/
@Autowired
private AuthenticationSuccessHandler msAuthenticationSuccessHandler;
/**
* 输入身份认证失败后后续处理对象
*/
@Autowired
private AuthenticationFailureHandler msAuthenticationFailureHandler;
/**
* 输入身份认证失败后后续处理对象
*/
@Autowired
private AuthenticationFailureHandler msAuthenticationFailureHandler;
/**
* 需要注入数据源
*/
@Autowired
@Qualifier("masterDataSource")
private DataSource dataSource;
/**
* 需要注入数据源
*/
@Autowired
@Qualifier("masterDataSource")
private DataSource dataSource;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private UserDetailsService userDetailsService;
/**
* 自定义身份认证配置
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//创建自定义的验证码过滤器,填入身份认证失败处理对象
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
validateCodeFilter.setAuthenticationFailureHandler(msAuthenticationFailureHandler);
validateCodeFilter.setSecurityProperties(securityProperties);
validateCodeFilter.afterPropertiesSet();
http
//添加自定义的验证码处理器,在用户密码校验过滤器之前
.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
//使用表单登录
.formLogin()
//自定义登录页面
.loginPage("/authentication/require")
//配置spring security登录接口的访问地址,用于在自定义的登录页面提交请求
.loginProcessingUrl("/user/login")
//定义身份认证成功后处理对象
.successHandler(msAuthenticationSuccessHandler)
//定义身份认证失败后处理对象
.failureHandler(msAuthenticationFailureHandler)
.and()
//记住我功能的相关配置
.rememberMe()
//设置token的存储方式
.tokenRepository(persistentTokenRepository())
//设置过期秒数
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
//设置获取用户信息后登录的方式
.userDetailsService(userDetailsService)
.and()
.authorizeRequests()//下面是权限配置
//当访问这个页面的时候不进行身份认证,对用户自定义的登录页也不进行权限认证
.antMatchers("/user/login", "/code/*", "/authentication/require", securityProperties.getBrowser().getHtml()).permitAll()
//任何请求
.anyRequest()
//都需要身份认证
.authenticated()
.and()
//暂时关闭掉跨站请求攻击防护
.csrf().disable();
/**
* 短信验证码登录的配置
*/
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
}
/**
* 自定义身份认证配置
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//创建自定义的验证码过滤器,填入身份认证失败处理对象
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
validateCodeFilter.setAuthenticationFailureHandler(msAuthenticationFailureHandler);
validateCodeFilter.setSecurityProperties(securityProperties);
validateCodeFilter.afterPropertiesSet();
http
//添加自定义的验证码处理器,在用户密码校验过滤器之前
.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
//使用表单登录
.formLogin()
//自定义登录页面
.loginPage(SecurityConstants.LOGIN_PAGE)
//配置spring security登录接口的访问地址,用于在自定义的登录页面提交请求
.loginProcessingUrl(SecurityConstants.FORM_LOGIN_PROCESSOR_URI)
//定义身份认证成功后处理对象
.successHandler(msAuthenticationSuccessHandler)
//定义身份认证失败后处理对象
.failureHandler(msAuthenticationFailureHandler)
.and()
//记住我功能的相关配置,是基于cookie实现的,所以是浏览器特有的功能
.rememberMe()
//设置token的存储方式
.tokenRepository(persistentTokenRepository())
//设置过期秒数
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
//设置获取用户信息后登录的方式
.userDetailsService(userDetailsService)
.and()
.authorizeRequests()//下面是权限配置
//当访问这个页面的时候不进行身份认证,对用户自定义的登录页也不进行权限认证
.antMatchers(SecurityConstants.MOBILE_LOGIN_PROCESSOR_URI
, SecurityConstants.FORM_LOGIN_PROCESSOR_URI
, SecurityConstants.GET_VALIDATE_CODE_URI
, SecurityConstants.LOGIN_PAGE
, securityProperties.getBrowser().getHtml())
.permitAll()
//任何请求
.anyRequest()
//都需要身份认证
.authenticated()
.and()
//暂时关闭掉跨站请求攻击防护
.csrf().disable()
//应用短信验证码登录的逻辑
.apply(smsCodeAuthenticationSecurityConfig);
/**
* 定义身份认证红密码的加密解密类的bean
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
/**
* 用于记住我功能
*
* @return
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//启动的时候创建表
jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
/**
* 定义身份认证红密码的加密解密类的bean
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 用于记住我功能
*
* @return
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//启动的时候创建表
jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
}
@@ -19,6 +19,7 @@ import java.io.IOException;
/**
* AuthenticationFailureHandler
* 身份认证失败的后续处理类
* @author lsy
*/
@Component("msAuthenticationFailureHandler")
public class MsAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@@ -17,6 +17,7 @@ import java.io.IOException;
/**
* 身份认证成功后的处理类
* @author lsy
*/
@Component("msAuthenticationSuccessHandler")
public class MsAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@@ -15,10 +15,10 @@ import org.springframework.stereotype.Component;
* 自定义根据用户名获取密码方法替换spring security的默认逻辑
* @author lsy
*/
@Component
public class MsUserDetialsServiceImpl implements UserDetailsService {
@Component("msUserDetailsServiceImpl")
public class MsUserDetailsServiceImpl implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(MsUserDetialsServiceImpl.class);
private static final Logger log = LoggerFactory.getLogger(MsUserDetailsServiceImpl.class);
@Autowired
@@ -1,5 +1,6 @@
package com.micro.fast.security.browser.controller;
import com.micro.fast.security.core.master.SecurityConstants;
import com.micro.fast.security.core.master.SecurityProperties;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@@ -39,7 +40,7 @@ public class BrowserSecurityController {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@RequestMapping("/authentication/require")
@RequestMapping(SecurityConstants.LOGIN_PAGE)
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
//根据当前请求获取上一次请求的缓存
@@ -57,7 +58,7 @@ public class BrowserSecurityController {
}
}
//如果是异步请求的话就放回404状态码和提示信息
return new String("访问的服务需要身份认证,需要引导用户到身份认证页面");
return "访问的服务需要身份认证,需要引导用户到身份认证页面";
}
}
@@ -4,10 +4,11 @@ import org.springframework.security.core.userdetails.User;
/**
* 用于根据spring security传入的username获取用户的详细信息接口
* @author lsy
*/
public interface UserService {
/**
*
* 根据用户的username获取用户的身份认证信息
* @param username
* @return user
*/
@@ -1,5 +1,6 @@
package com.micro.fast.security.core.authentication.mobile;
import com.micro.fast.security.core.master.SecurityConstants;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
@@ -10,16 +11,17 @@ 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;
/**
* 从请求中获取手机号参数的key
*/
private String mobileParameter = SecurityConstants.MOBILE_LOGIN_PROCESSOR_PARAM_MOBILE_KEY;
/**
* 是不是只处理post请求
@@ -27,10 +29,10 @@ public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessin
private boolean postOnly = true;
/**
* 对/authentication/mobile路径的post请求进行身份认证
* 对/authentication/mobile路径的post请求进行身份登录操作
*/
public SmsCodeAuthenticationFilter() {
super(new AntPathRequestMatcher(AUTHENTICATION_MOBILE_URL, "POST"));
super(new AntPathRequestMatcher(SecurityConstants.MOBILE_LOGIN_PROCESSOR_URI, "POST"));
}
/**
@@ -0,0 +1,52 @@
package com.micro.fast.security.core.authentication.mobile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
/**
* @author lsy
* 短信验证码登录的配置类
*/
@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,HttpSecurity>{
@Autowired
@Qualifier("msAuthenticationSuccessHandler")
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
@Qualifier("msAuthenticationFailureHandler")
private AuthenticationFailureHandler authenticationFailureHandler;
//todo 需要改成根据手机号获取用户的信息
@Autowired
@Qualifier("msUserDetailsServiceImpl")
private UserDetailsService userDetailsService;
@Override
public void configure(HttpSecurity http) throws Exception {
SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
//将短信验证码登录过滤器放入AuthenticationManager,方便后续调用
smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
smsCodeAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
//将根据手机号获取用户信息的对象放入短信验证码的provider中
SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
//将自定义的provider放入authenticaionManger管理的集合中去
http.authenticationProvider(smsCodeAuthenticationProvider)
//将自定义的过滤器添加到用户账号密码校验过滤器后面
.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
@@ -12,7 +12,6 @@ public class BrowserProperties {
/**
* 用户自自定义的登录页面,不能为空
*
*/
@NotBlank
private String html;
@@ -0,0 +1,39 @@
package com.micro.fast.security.core.master;
/**
* 身份认证框架常亮类
* @author lsy
*/
public interface SecurityConstants {
/**
* 触发身份认证请求的时候跳转到的uri
*/
String LOGIN_PAGE = "/authentication/require";
/**
* 处理表单登录请求的uri
*/
String FORM_LOGIN_PROCESSOR_URI = "/user/login";
/**
* 处理手机短信登录的uri
*/
String MOBILE_LOGIN_PROCESSOR_URI = "/authentication/mobile";
/**
* 手机短信身份认证时从请求中获取手机号的key
*/
String MOBILE_LOGIN_PROCESSOR_PARAM_MOBILE_KEY = "mobile";
/**
* 获取验证码的uri
*/
String GET_VALIDATE_CODE_URI = SecurityConstants.GET_VALIDATE_CODE_PREFIX+"*";
/**
* 获取验证码请求的前缀
*/
String GET_VALIDATE_CODE_PREFIX = "/code/";
}
@@ -1,5 +1,7 @@
package com.micro.fast.security.core.validate.code.config.param;
import com.micro.fast.security.core.master.SecurityConstants;
/**
* 图形验证码默认的配置
* @author lsy
@@ -19,10 +21,9 @@ public class ImageCodeProperties extends SmsCodeProperties{
*/
public ImageCodeProperties() {
setLength(4);
setUrl(SecurityConstants.FORM_LOGIN_PROCESSOR_URI);
}
public int getWidth() {
return width;
}
@@ -1,5 +1,7 @@
package com.micro.fast.security.core.validate.code.config.param;
import com.micro.fast.security.core.master.SecurityConstants;
/**
* @author lsy
*/
@@ -16,7 +18,7 @@ public class SmsCodeProperties {
/**
* 需要进行验证码校验的url
*/
private String url = "/user/login";
private String url = SecurityConstants.MOBILE_LOGIN_PROCESSOR_URI;
public String getUrl() {
return url;
@@ -1,5 +1,6 @@
package com.micro.fast.security.core.validate.code.controller;
import com.micro.fast.security.core.master.SecurityConstants;
import com.micro.fast.security.core.validate.code.processor.ValidateCodeProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.ServletRequestBindingException;
@@ -29,7 +30,7 @@ public class ValidateCodeController {
private static final String VALIDATE_CODE_PROCESSOR_SUFFIX = "ValidateCodeProcessor";
@GetMapping("/code/{type}")
@GetMapping(SecurityConstants.GET_VALIDATE_CODE_PREFIX+"{type}")
public void createCode(HttpServletResponse response, HttpServletRequest request,@PathVariable("type") String type) throws IOException, ServletRequestBindingException {
//create save send ValidateCode
validateCodeProcessors.get(type+VALIDATE_CODE_PROCESSOR_SUFFIX).create(new ServletWebRequest(request,response));
@@ -1,132 +0,0 @@
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;
}
}
@@ -1,5 +1,6 @@
package com.micro.fast.security.core.validate.code.filter;
import com.micro.fast.security.core.master.SecurityConstants;
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;
@@ -18,119 +19,151 @@ 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;
import java.util.*;
/**
* 图片验证码校验过滤器
*
* @author LSY
*/
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
private AuthenticationFailureHandler authenticationFailureHandler;
/**
* session操作工具类
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
public static final String IMAGE = "IMAGE";
public static final String SMS = "SMS";
private SecurityProperties securityProperties;
private AuthenticationFailureHandler authenticationFailureHandler;
private Set<String> filterUrl = new HashSet<String>();
/**
* session操作工具类
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
private static final String REQUEST_METHOD = "post";
private SecurityProperties securityProperties;
/**
* 初始化filter url
* @throws ServletException
*/
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
String url = securityProperties.getCode().getImage().getUrl();
String[] urls = url.split(",");
for (String s : urls) {
filterUrl.add(s);
}
filterUrl.add("/user/login");
/**
* 需要进行验证码校验的路径和校验的类型的map
*/
private Map<String, String> filterUrlMap = new HashMap<>();
private static final String REQUEST_METHOD = "post";
/**
* 初始化filterUrlMap
*
* @throws ServletException
*/
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
String imageUrl = securityProperties.getCode().getImage().getUrl();
putValidateUrlsToMap(imageUrl, IMAGE);
String smsUrl = securityProperties.getCode().getSms().getUrl();
putValidateUrlsToMap(smsUrl, SMS);
}
/**
* 将需要校验的请求路径和校验类类型存入filterUrlMap中
*
* @param patterns
* @param type
*/
private void putValidateUrlsToMap(String patterns, String type) {
String[] urls = patterns.split(",");
for (String url : urls) {
filterUrlMap.put(url, type);
}
@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);
if (IMAGE.equals(type)) {
filterUrlMap.put(SecurityConstants.FORM_LOGIN_PROCESSOR_URI, type);
}
/**
* 验证码校验
* @param servletWebRequest
*/
private void validate(ServletWebRequest servletWebRequest) {
ValidateCode validateCode = (ValidateCode)(sessionStrategy.getAttribute(servletWebRequest, ValidateCodeProcessor.SESSION_KEY_PREFIX+"IMAGE"));
String code = servletWebRequest.getParameter("imageCode");
//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+"IMAGE");
if (SMS.equals(type)) {
filterUrlMap.put(SecurityConstants.MOBILE_LOGIN_PROCESSOR_URI, type);
}
}
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return authenticationFailureHandler;
}
@Override
protected void doFilterInternal(
HttpServletRequest httpServletRequest
, HttpServletResponse httpServletResponse
, FilterChain filterChain)
throws ServletException, IOException {
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
//判断是否需要验证码校验
boolean filter = false;
//进行校验的类型
String validateType = "";
AntPathMatcher antPathMatcher = new AntPathMatcher();
Set<String> filterUrls = filterUrlMap.keySet();
Iterator<String> iterator = filterUrls.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (antPathMatcher.match(next, httpServletRequest.getRequestURI())) {
filter = true;
validateType = filterUrlMap.get(next);
break;
}
}
//在登录post请求的时候对验证码进行校验
if (filter && StringUtils.endsWithIgnoreCase(httpServletRequest.getMethod(), REQUEST_METHOD)) {
try {
//校验验证码是否正确
validate(new ServletWebRequest(httpServletRequest),validateType);
} catch (ValidateCodeException e) {
//进行身份认证失败后的处理
authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
//身份认证失败的时候直接return返回给页面信息,不进行后续的
return;
}
}
//执行后续的过滤器
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
public Set<String> getFilterUrl() {
return filterUrl;
}
/**
* 验证码校验
*
* @param servletWebRequest
* @param validateType
*/
private void validate(ServletWebRequest servletWebRequest, String validateType) {
ValidateCode validateCode = (ValidateCode) (sessionStrategy.getAttribute(servletWebRequest, ValidateCodeProcessor.SESSION_KEY_PREFIX + validateType));
String code = servletWebRequest.getParameter(validateType.toLowerCase()+"Code");
public void setFilterUrl(Set<String> filterUrl) {
this.filterUrl = filterUrl;
//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 + validateType);
}
public SecurityProperties getSecurityProperties() {
return securityProperties;
}
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return authenticationFailureHandler;
}
public void setSecurityProperties(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
}
public Map<String, String> getFilterUrlMap() {
return filterUrlMap;
}
public void setFilterUrlMap(Map<String, String> filterUrlMap) {
this.filterUrlMap = filterUrlMap;
}
public SecurityProperties getSecurityProperties() {
return securityProperties;
}
public void setSecurityProperties(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
}
@@ -16,6 +16,7 @@ public interface ValidateCodeProcessor {
*/
String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
/**
* 创建验证码,将验证码写入session中,输出验证码
* @param servletWebRequest 封装请求和响应
@@ -1,5 +1,6 @@
package com.micro.fast.security.core.validate.code.processor.impl;
import com.micro.fast.security.core.master.SecurityConstants;
import com.micro.fast.security.core.validate.code.pojo.ValidateCode;
import com.micro.fast.security.core.validate.code.service.SendSmsCode;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,12 +18,10 @@ public class SmsValidateCodeProcessor extends AbstractValidateCodeProcessor{
@Autowired
private SendSmsCode sendSmsCode;
private static final String PHONE = "phone";
@Override
public void send(ServletWebRequest servletWebRequest, ValidateCode validateCode) throws ServletRequestBindingException {
//获取phone,请求中必须包含用户的手机号
String phone = ServletRequestUtils.getRequiredStringParameter(servletWebRequest.getRequest(), PHONE);
String phone = ServletRequestUtils.getRequiredStringParameter(servletWebRequest.getRequest(), SecurityConstants.MOBILE_LOGIN_PROCESSOR_PARAM_MOBILE_KEY);
//发送验证码的逻辑
sendSmsCode.sendCode(phone,validateCode.getCode());
}
@@ -6,9 +6,12 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* swagger文档的默认的访问地址是/swagger-ui.html
*/
@SpringBootApplication
@RestController
@EnableSwagger2//默认的访问地址是/swagger-ui.html
@EnableSwagger2
public class BootStarterSecurityDemoApplication {
public static void main(String[] args) {
@@ -12,8 +12,14 @@ import org.springframework.cloud.config.server.EnableConfigServer;
* @since 1.0
*/
@SpringBootApplication
@EnableConfigServer //开启配置中心相关服务
@EnableDiscoveryClient//允许注册到注册中心
/**
* 开启配置中心相关服务
*/
@EnableConfigServer
/**
* 允许注册到注册中心
*/
@EnableDiscoveryClient
public class ConfigServerApplication {
/**
@@ -0,0 +1,5 @@
<root>
<item name='org.springframework.security.authentication.AuthenticationManager'>
<annotation name='java.lang.Deprecated'/>
</item>
</root>