users = new ArrayList<>();
67 | users.add(new User("Hehe", "123456"));
68 | users.add(new User("Tom", "654321"));
69 | users.add(new User("Rose", "142536"));
70 | return users;
71 | }
72 |
73 | // 正则表达式
74 | @GetMapping("/{id:\\d+}")
75 | public User getInfo(@PathVariable String id) {
76 | User user = new User();
77 | user.setUserName("tom");
78 | return user;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/security-core/src/main/java/com/whyalwaysmea/core/validate/image/ImageValidateCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package com.whyalwaysmea.core.validate.image;
2 |
3 | import com.whyalwaysmea.core.properties.SecurityProperties;
4 | import com.whyalwaysmea.core.validate.ValidateCodeGenerator;
5 | import org.springframework.web.bind.ServletRequestUtils;
6 | import org.springframework.web.context.request.ServletWebRequest;
7 |
8 | import java.awt.*;
9 | import java.awt.image.BufferedImage;
10 | import java.util.Random;
11 |
12 | /**
13 | * @Author: HanLong
14 | * @Date: Create in 2018/3/24 9:51
15 | * @Description:
16 | */
17 | public class ImageValidateCodeGenerator implements ValidateCodeGenerator {
18 |
19 | private SecurityProperties securityProperties;
20 |
21 | /**
22 | * 生成图形验证码
23 | * @param request
24 | * @return
25 | */
26 | public ImageCode generate(ServletWebRequest request) {
27 | int width = ServletRequestUtils.getIntParameter(request.getRequest(), "width", securityProperties.getCode().getImage().getWidth());
28 | int height = ServletRequestUtils.getIntParameter(request.getRequest(), "height", securityProperties.getCode().getImage().getHeight());
29 | BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
30 |
31 | Graphics g = image.getGraphics();
32 |
33 | Random random = new Random();
34 |
35 | g.setColor(getRandColor(200, 250));
36 | g.fillRect(0, 0, width, height);
37 | g.setFont(new Font("Times New Roman", Font.ITALIC, 20));
38 | g.setColor(getRandColor(160, 200));
39 | for (int i = 0; i < 155; i++) {
40 | int x = random.nextInt(width);
41 | int y = random.nextInt(height);
42 | int xl = random.nextInt(12);
43 | int yl = random.nextInt(12);
44 | g.drawLine(x, y, x + xl, y + yl);
45 | }
46 |
47 | String sRand = "";
48 | int length = ServletRequestUtils.getIntParameter(request.getRequest(), "length", securityProperties.getCode().getImage().getLength());
49 | for (int i = 0; i < length; i++) {
50 | String rand = String.valueOf(random.nextInt(10));
51 | sRand += rand;
52 | g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
53 | g.drawString(rand, 13 * i + 6, 16);
54 | }
55 |
56 | g.dispose();
57 |
58 | return new ImageCode(image, sRand, securityProperties.getCode().getImage().getExpireIn());
59 |
60 | }
61 |
62 | /**
63 | * 生成随机背景条纹
64 | *
65 | * @param fc
66 | * @param bc
67 | * @return
68 | */
69 | private Color getRandColor(int fc, int bc) {
70 | Random random = new Random();
71 | if (fc > 255) {
72 | fc = 255;
73 | }
74 | if (bc > 255) {
75 | bc = 255;
76 | }
77 | int r = fc + random.nextInt(bc - fc);
78 | int g = fc + random.nextInt(bc - fc);
79 | int b = fc + random.nextInt(bc - fc);
80 | return new Color(r, g, b);
81 | }
82 |
83 | public void setSecurityProperties(SecurityProperties securityProperties) {
84 | this.securityProperties = securityProperties;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/security-core/src/main/java/com/whyalwaysmea/core/authentication/sms/SmsCodeAuthenticationFilter.java:
--------------------------------------------------------------------------------
1 | package com.whyalwaysmea.core.authentication.sms;
2 |
3 | import com.whyalwaysmea.core.properties.SecurityConstants;
4 | import org.springframework.security.authentication.AuthenticationServiceException;
5 | import org.springframework.security.core.Authentication;
6 | import org.springframework.security.core.AuthenticationException;
7 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
8 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
9 | import org.springframework.util.Assert;
10 |
11 | import javax.servlet.http.HttpServletRequest;
12 | import javax.servlet.http.HttpServletResponse;
13 |
14 | /**
15 | * @Author: HanLong
16 | * @Date: Create in 2018/3/24 15:30
17 | * @Description: 短信验证码过滤器 类似于{@link AuthenticationFilter}
18 | */
19 | public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
20 |
21 | /**
22 | * 请求中的参数
23 | */
24 | private String mobileParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE;
25 |
26 | /**
27 | * 只接受POST方式
28 | */
29 | private boolean postOnly = true;
30 |
31 | public SmsCodeAuthenticationFilter() {
32 | super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, "POST"));
33 | }
34 |
35 | public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
36 | throws AuthenticationException {
37 | // 请求方式校验
38 | if (postOnly && !request.getMethod().equals("POST")) {
39 | throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
40 | }
41 |
42 | // 获取请求中的参数值
43 | String mobile = obtainMobile(request);
44 |
45 | if (mobile == null) {
46 | mobile = "";
47 | }
48 |
49 | mobile = mobile.trim();
50 |
51 | SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
52 |
53 | // Allow subclasses to set the "details" property
54 | setDetails(request, authRequest);
55 |
56 | /**
57 | * 通过 {@link ProvierMagaer} 调用{@link SmsCodeAuthenticationProvider}
58 | */
59 | return this.getAuthenticationManager().authenticate(authRequest);
60 | }
61 |
62 |
63 | /**
64 | * 获取手机号
65 | */
66 | protected String obtainMobile(HttpServletRequest request) {
67 | return request.getParameter(mobileParameter);
68 | }
69 |
70 | protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
71 | authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
72 | }
73 |
74 | /**
75 | * Sets the parameter name which will be used to obtain the username from
76 | * the login request.
77 | *
78 | * @param usernameParameter
79 | * the parameter name. Defaults to "username".
80 | */
81 | public void setMobileParameter(String usernameParameter) {
82 | Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
83 | this.mobileParameter = usernameParameter;
84 | }
85 |
86 |
87 | /**
88 | * Defines whether only HTTP POST requests will be allowed by this filter.
89 | * If set to true, and an authentication request is received which is not a
90 | * POST request, an exception will be raised immediately and authentication
91 | * will not be attempted. The unsuccessfulAuthentication() method
92 | * will be called as if handling a failed authentication.
93 | *
94 | * Defaults to true but may be overridden by subclasses.
95 | */
96 | public void setPostOnly(boolean postOnly) {
97 | this.postOnly = postOnly;
98 | }
99 |
100 | public final String getMobileParameter() {
101 | return mobileParameter;
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/security-browser/src/main/java/com/whyalwaysmea/browser/config/BrowerSecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.whyalwaysmea.browser.config;
2 |
3 | import com.whyalwaysmea.core.authentication.AuthorizeConfigManager;
4 | import com.whyalwaysmea.core.authentication.sms.AbstractChannelSecurityConfig;
5 | import com.whyalwaysmea.core.properties.SecurityConstants;
6 | import com.whyalwaysmea.core.properties.SecurityProperties;
7 | import com.whyalwaysmea.core.authentication.sms.SmsCodeAuthenticationSecurityConfig;
8 | import com.whyalwaysmea.core.validate.ValidateCodeFilter;
9 | import com.whyalwaysmea.core.validate.ValidateCodeSecurityConfig;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.context.annotation.Bean;
12 | import org.springframework.context.annotation.Configuration;
13 | import org.springframework.http.HttpMethod;
14 | import org.springframework.security.authentication.AuthenticationManager;
15 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
16 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
17 | import org.springframework.security.core.userdetails.UserDetailsService;
18 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
19 | import org.springframework.security.crypto.password.PasswordEncoder;
20 | import org.springframework.security.web.authentication.AuthenticationFailureHandler;
21 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
22 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
23 | import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
24 | import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
25 | import org.springframework.security.web.session.InvalidSessionStrategy;
26 | import org.springframework.security.web.session.SessionInformationExpiredStrategy;
27 |
28 | import javax.sql.DataSource;
29 |
30 | /**
31 | * @Author: HanLong
32 | * @Date: Create in 2018/3/17 22:10
33 | * @Description:
34 | */
35 | @Configuration
36 | public class BrowerSecurityConfig extends AbstractChannelSecurityConfig {
37 |
38 | @Autowired
39 | private SecurityProperties securityProperties;
40 |
41 | @Autowired
42 | private DataSource dataSource;
43 |
44 | @Autowired
45 | private UserDetailsService myUserDetailsService;
46 |
47 | @Autowired
48 | private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
49 |
50 | @Autowired
51 | private ValidateCodeSecurityConfig validateCodeSecurityConfig;
52 |
53 | @Autowired
54 | private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;
55 |
56 | @Autowired
57 | private InvalidSessionStrategy invalidSessionStrategy;
58 |
59 | @Autowired
60 | private AuthorizeConfigManager authorizeConfigManager;
61 |
62 | /**
63 | * 配置TokenRepository
64 | * @return
65 | */
66 | @Bean
67 | public PersistentTokenRepository persistentTokenRepository() {
68 | JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
69 | jdbcTokenRepository.setDataSource(dataSource);
70 | // jdbcTokenRepository.setCreateTableOnStartup(true);
71 | return jdbcTokenRepository;
72 | }
73 |
74 | @Override
75 | protected void configure(HttpSecurity http) throws Exception {
76 |
77 | applyPasswordAuthenticationConfig(http);
78 |
79 | http.apply(validateCodeSecurityConfig)
80 | .and()
81 | .apply(smsCodeAuthenticationSecurityConfig)
82 | .and()
83 | .rememberMe() // 记住我相关配置
84 | .tokenRepository(persistentTokenRepository())
85 | .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
86 | .userDetailsService(myUserDetailsService)
87 | .and()
88 | .sessionManagement()
89 | .invalidSessionStrategy(invalidSessionStrategy) // session超时跳转
90 | .maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions()) // 最大并发session
91 | .maxSessionsPreventsLogin(securityProperties.getBrowser().getSession().isMaxSessionsPreventsLogin()) // 是否阻止新的登录
92 | .expiredSessionStrategy(sessionInformationExpiredStrategy) // 并发session失效原因
93 | .and()
94 | .and()
95 | .csrf().disable(); // 关闭csrf防护
96 |
97 | authorizeConfigManager.config(http.authorizeRequests());
98 |
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/security-core/src/main/java/com/whyalwaysmea/core/validate/impl/AbstractValidateCodeProcessor.java:
--------------------------------------------------------------------------------
1 | package com.whyalwaysmea.core.validate.impl;
2 |
3 | /**
4 | * @Author: HanLong
5 | * @Date: Create in 2018/3/24 14:10
6 | * @Description: 抽象的验证码处理器
7 | */
8 |
9 | import com.whyalwaysmea.core.validate.*;
10 | import org.apache.commons.lang.StringUtils;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.social.connect.web.HttpSessionSessionStrategy;
13 | import org.springframework.social.connect.web.SessionStrategy;
14 | import org.springframework.web.bind.ServletRequestBindingException;
15 | import org.springframework.web.bind.ServletRequestUtils;
16 | import org.springframework.web.context.request.ServletWebRequest;
17 |
18 | import java.util.Map;
19 |
20 | public abstract class AbstractValidateCodeProcessor implements ValidateCodeProcessor {
21 |
22 | /**
23 | * 操作session的工具类
24 | */
25 | private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
26 | /**
27 | * 收集系统中所有的 {@link ValidateCodeGenerator} 接口的实现。
28 | */
29 | @Autowired
30 | private Map validateCodeGenerators;
31 |
32 | @Override
33 | public void create(ServletWebRequest request) throws Exception {
34 | C validateCode = generate(request);
35 | save(request, validateCode);
36 | send(request, validateCode);
37 | }
38 |
39 | /**
40 | * 生成校验码
41 | *
42 | * @param request
43 | * @return
44 | */
45 | @SuppressWarnings("unchecked")
46 | private C generate(ServletWebRequest request) {
47 | String type = getValidateCodeType(request).toString().toLowerCase();
48 | String generatorName = type + ValidateCodeGenerator.class.getSimpleName();
49 | ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(generatorName);
50 | if (validateCodeGenerator == null) {
51 | throw new ValidateCodeException("验证码生成器" + generatorName + "不存在");
52 | }
53 | return (C) validateCodeGenerator.generate(request);
54 | }
55 |
56 | /**
57 | * 保存校验码
58 | *
59 | * @param request
60 | * @param validateCode
61 | */
62 | private void save(ServletWebRequest request, C validateCode) {
63 | //TODO 直接存imageCode会出错,因为BufferedImage无法序列化的
64 | // sessionStrategy.setAttribute(request, getSessionKey(request), validateCode);
65 | sessionStrategy.setAttribute(request, getSessionKey(request), validateCode.getCode());
66 | }
67 |
68 | /**
69 | * 构建验证码放入session时的key
70 | *
71 | * @param request
72 | * @return
73 | */
74 | private String getSessionKey(ServletWebRequest request) {
75 | return SESSION_KEY_PREFIX + getValidateCodeType(request).toString().toUpperCase();
76 | }
77 |
78 | /**
79 | * 发送校验码,由子类实现
80 | *
81 | * @param request
82 | * @param validateCode
83 | * @throws Exception
84 | */
85 | protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;
86 |
87 | /**
88 | * 根据请求的url获取校验码的类型
89 | *
90 | * @param request
91 | * @return
92 | */
93 | private ValidateCodeType getValidateCodeType(ServletWebRequest request) {
94 | String type = StringUtils.substringBefore(getClass().getSimpleName(), "CodeProcessor");
95 | return ValidateCodeType.valueOf(type.toUpperCase());
96 | }
97 |
98 | @SuppressWarnings("unchecked")
99 | @Override
100 | public void validate(ServletWebRequest request) {
101 |
102 | ValidateCodeType processorType = getValidateCodeType(request);
103 | String sessionKey = getSessionKey(request);
104 |
105 | C codeInSession = (C) sessionStrategy.getAttribute(request, sessionKey);
106 |
107 | String codeInRequest;
108 | try {
109 | codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
110 | processorType.getParamNameOnValidate());
111 | } catch (ServletRequestBindingException e) {
112 | throw new ValidateCodeException("获取验证码的值失败");
113 | }
114 |
115 | if (StringUtils.isBlank(codeInRequest)) {
116 | throw new ValidateCodeException(processorType + "验证码的值不能为空");
117 | }
118 |
119 | if (codeInSession == null) {
120 | throw new ValidateCodeException(processorType + "验证码不存在");
121 | }
122 |
123 | if (codeInSession.isExpried()) {
124 | sessionStrategy.removeAttribute(request, sessionKey);
125 | throw new ValidateCodeException(processorType + "验证码已过期");
126 | }
127 |
128 | if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
129 | throw new ValidateCodeException(processorType + "验证码不匹配");
130 | }
131 |
132 | sessionStrategy.removeAttribute(request, sessionKey);
133 | }
134 |
135 | }
--------------------------------------------------------------------------------
/security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeFilter.java:
--------------------------------------------------------------------------------
1 | package com.whyalwaysmea.core.validate;
2 |
3 | import com.whyalwaysmea.core.properties.SecurityConstants;
4 | import com.whyalwaysmea.core.properties.SecurityProperties;
5 | import org.apache.commons.lang.StringUtils;
6 | import org.springframework.beans.factory.InitializingBean;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.security.web.authentication.AuthenticationFailureHandler;
9 | import org.springframework.social.connect.web.HttpSessionSessionStrategy;
10 | import org.springframework.social.connect.web.SessionStrategy;
11 | import org.springframework.stereotype.Component;
12 | import org.springframework.util.AntPathMatcher;
13 | import org.springframework.util.PathMatcher;
14 | import org.springframework.web.bind.ServletRequestBindingException;
15 | import org.springframework.web.bind.ServletRequestUtils;
16 | import org.springframework.web.context.request.ServletWebRequest;
17 | import org.springframework.web.filter.OncePerRequestFilter;
18 |
19 | import javax.servlet.FilterChain;
20 | import javax.servlet.ServletException;
21 | import javax.servlet.http.HttpServletRequest;
22 | import javax.servlet.http.HttpServletResponse;
23 | import java.io.IOException;
24 | import java.util.HashMap;
25 | import java.util.HashSet;
26 | import java.util.Map;
27 | import java.util.Set;
28 |
29 | /**
30 | * @Author: HanLong
31 | * @Date: Create in 2018/3/21 21:04
32 | * @Description: 验证码过滤器
33 | */
34 | @Component("validateCodeFilter")
35 | public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{
36 |
37 | /**
38 | * 验证码校验失败处理器
39 | */
40 | @Autowired
41 | private AuthenticationFailureHandler authenticationFailureHandler;
42 |
43 | /**
44 | * 系统中的校验码处理器
45 | */
46 | @Autowired
47 | private ValidateCodeProcessorHolder validateCodeProcessorHolder;
48 |
49 | /**
50 | * 存放所有需要校验验证码的url
51 | */
52 | private Map urlMap = new HashMap<>();
53 |
54 | /**
55 | * 系统配置信息
56 | */
57 | @Autowired
58 | private SecurityProperties securityProperties;
59 |
60 | private AntPathMatcher pathMatcher = new AntPathMatcher();
61 |
62 | @Override
63 | public void afterPropertiesSet() throws ServletException {
64 | super.afterPropertiesSet();
65 | urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM, ValidateCodeType.IMAGE);
66 | addUrlToMap(securityProperties.getCode().getImage().getUrl(), ValidateCodeType.IMAGE);
67 |
68 | urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, ValidateCodeType.SMS);
69 | addUrlToMap(securityProperties.getCode().getSms().getUrl(), ValidateCodeType.SMS);
70 | }
71 |
72 | /**
73 | * 讲系统中配置的需要校验验证码的URL根据校验的类型放入map
74 | *
75 | * @param urlString
76 | * @param type
77 | */
78 | protected void addUrlToMap(String urlString, ValidateCodeType type) {
79 | if (StringUtils.isNotBlank(urlString)) {
80 | String[] urls = StringUtils.splitByWholeSeparatorPreserveAllTokens(urlString, ",");
81 | for (String url : urls) {
82 | urlMap.put(url, type);
83 | }
84 | }
85 | }
86 |
87 | @Override
88 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
89 | ValidateCodeType type = getValidateCodeType(request);
90 | if (type != null) {
91 | logger.info("校验请求(" + request.getRequestURI() + ")中的验证码,验证码类型" + type);
92 | try {
93 | /*validateCodeProcessorHolder.findValidateCodeProcessor(type)
94 | .validate(new ServletWebRequest(request, response));*/
95 | logger.info("验证码校验通过");
96 | } catch (ValidateCodeException exception) {
97 | authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
98 | return;
99 | }
100 | }
101 |
102 | chain.doFilter(request, response);
103 |
104 | }
105 |
106 | /**
107 | * 获取校验码的类型,如果当前请求不需要校验,则返回null
108 | *
109 | * @param request
110 | * @return
111 | */
112 | private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
113 | ValidateCodeType result = null;
114 | if (!StringUtils.equalsIgnoreCase(request.getMethod(), "get")) {
115 | Set urls = urlMap.keySet();
116 | for (String url : urls) {
117 | if (pathMatcher.match(url, request.getRequestURI())) {
118 | result = urlMap.get(url);
119 | }
120 | }
121 | }
122 | return result;
123 | }
124 |
125 |
126 | public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
127 | this.authenticationFailureHandler = authenticationFailureHandler;
128 | }
129 |
130 | public void setSecurityProperties(SecurityProperties securityProperties) {
131 | this.securityProperties = securityProperties;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------