├── security-demo ├── src │ ├── main │ │ ├── resources │ │ │ ├── resources │ │ │ │ ├── index.html │ │ │ │ └── demo-login.html │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── whyalwaysmea │ │ │ ├── service │ │ │ ├── HelloService.java │ │ │ └── impl │ │ │ │ └── HelloServiceImpl.java │ │ │ ├── exception │ │ │ └── UserNotExistException.java │ │ │ ├── code │ │ │ └── ImageCodeGenerator.java │ │ │ ├── validator │ │ │ ├── MyConstraint.java │ │ │ ├── ValidateException.java │ │ │ ├── MyConstraintValidator.java │ │ │ └── ValidateAspect.java │ │ │ ├── dto │ │ │ ├── UserQueryCondition.java │ │ │ └── User.java │ │ │ ├── DemoApplication.java │ │ │ ├── security │ │ │ ├── DemoAuthorizeConfigProvider.java │ │ │ ├── RbacPermission.java │ │ │ └── MyUserDetailsService.java │ │ │ └── web │ │ │ ├── controller │ │ │ ├── AsyncController.java │ │ │ ├── ControllerExceptionHandler.java │ │ │ └── UserController.java │ │ │ ├── filter │ │ │ └── TimeFilter.java │ │ │ ├── aspect │ │ │ └── TimeAspect.java │ │ │ ├── config │ │ │ └── WebConfig.java │ │ │ └── interceptor │ │ │ └── TimeInterceptor.java │ └── test │ │ └── java │ │ └── com │ │ └── whyalwaysmea │ │ └── UserControllerTest.java └── pom.xml ├── security-app ├── src │ └── main │ │ └── java │ │ └── com │ │ └── whyalwaysmea │ │ └── app │ │ ├── App.java │ │ ├── MyResourceServerConfig.java │ │ ├── MyAuthorizationServerConfig.java │ │ └── authentication │ │ ├── MyAuthenctiationSuccessHandler.java │ │ └── MyAuthenctiationFailureHandler.java └── pom.xml ├── security-core ├── src │ └── main │ │ └── java │ │ └── com │ │ └── whyalwaysmea │ │ └── core │ │ ├── validate │ │ ├── sms │ │ │ ├── SmsCodeSender.java │ │ │ ├── DefaultSmsCodeSender.java │ │ │ ├── SmsCodeProcessor.java │ │ │ └── SmsCodeGenerator.java │ │ ├── ValidateCodeGenerator.java │ │ ├── ValidateCodeException.java │ │ ├── ValidateCodeProcessor.java │ │ ├── image │ │ │ ├── ImageCodeProcessor.java │ │ │ ├── ImageCode.java │ │ │ └── ImageValidateCodeGenerator.java │ │ ├── ValidateCodeType.java │ │ ├── ValidateCodeProcessorHolder.java │ │ ├── ValidateCodeSecurityConfig.java │ │ ├── ValidateCode.java │ │ ├── ValidateCodeBeanConfig.java │ │ ├── ValidateCodeController.java │ │ ├── impl │ │ │ └── AbstractValidateCodeProcessor.java │ │ └── ValidateCodeFilter.java │ │ ├── properties │ │ ├── LoginResponseType.java │ │ ├── ValidateCodeProperties.java │ │ ├── ImageProperties.java │ │ ├── SmsCodeProperties.java │ │ ├── SecurityProperties.java │ │ ├── SessionProperties.java │ │ ├── SecurityConstants.java │ │ └── BrowserProperties.java │ │ ├── dto │ │ └── BaseResponse.java │ │ ├── authentication │ │ ├── AuthorizeConfigProvider.java │ │ ├── AuthorizeConfigManager.java │ │ ├── MyAuthorizeConfigManager.java │ │ ├── MyAuthorizeConfigProvider.java │ │ └── sms │ │ │ ├── AbstractChannelSecurityConfig.java │ │ │ ├── SmsCodeAuthenticationToken.java │ │ │ ├── SmsCodeAuthenticationProvider.java │ │ │ ├── SmsCodeAuthenticationSecurityConfig.java │ │ │ └── SmsCodeAuthenticationFilter.java │ │ └── SecurityCoreConfig.java └── pom.xml ├── .gitignore ├── README.md ├── security-browser ├── src │ └── main │ │ ├── resources │ │ └── resources │ │ │ └── login.html │ │ └── java │ │ └── com │ │ └── whyalwaysmea │ │ └── browser │ │ ├── session │ │ ├── MyInvalidSessionStrategy.java │ │ ├── MyExpiredSessionStrategy.java │ │ └── AbstractSessionStrategy.java │ │ ├── config │ │ ├── BrowserSecurityBeanConfig.java │ │ └── BrowerSecurityConfig.java │ │ ├── authentication │ │ ├── MyAuthenctiationSuccessHandler.java │ │ └── MyAuthenctiationFailureHandler.java │ │ └── BrowserSecurityController.java └── pom.xml └── pom.xml /security-demo/src/main/resources/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 7 | 8 | index 9 | 10 | -------------------------------------------------------------------------------- /security-app/src/main/java/com/whyalwaysmea/app/App.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.app; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/27 21:55 6 | * @Description: 7 | */ 8 | public class App { 9 | } 10 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/service/HelloService.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.service; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/16 20:50 6 | * @Description: 7 | */ 8 | public interface HelloService { 9 | 10 | String greeting(String name); 11 | } 12 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/sms/SmsCodeSender.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate.sms; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/24 11:09 6 | * @Description: 7 | */ 8 | public interface SmsCodeSender { 9 | 10 | void send(String phone, String code); 11 | } 12 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/properties/LoginResponseType.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.properties; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/18 17:14 6 | * @Description: 登录成功后的处理 7 | */ 8 | public enum LoginResponseType { 9 | 10 | /** 11 | * 跳转 12 | */ 13 | REDIRECT, 14 | 15 | /** 16 | * 返回json 17 | */ 18 | JSON 19 | 20 | } -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeGenerator.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import org.springframework.web.context.request.ServletWebRequest; 4 | 5 | /** 6 | * @Author: HanLong 7 | * @Date: Create in 2018/3/24 9:51 8 | * @Description: 9 | */ 10 | public interface ValidateCodeGenerator { 11 | 12 | ValidateCode generate(ServletWebRequest request); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | .idea 22 | target/ 23 | 24 | *.iml 25 | /.idea/ 26 | /out/ 27 | 28 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 29 | hs_err_pid* 30 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeException.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | 5 | /** 6 | * @Author: HanLong 7 | * @Date: Create in 2018/3/21 21:11 8 | * @Description: 9 | */ 10 | public class ValidateCodeException extends AuthenticationException { 11 | 12 | public ValidateCodeException(String msg) { 13 | super(msg); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /security-app/src/main/java/com/whyalwaysmea/app/MyResourceServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.app; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 5 | 6 | /** 7 | * @Author: HanLong 8 | * @Date: Create in 2018/3/28 22:16 9 | * @Description: 资源服务认证 10 | */ 11 | @Configuration 12 | @EnableResourceServer 13 | public class MyResourceServerConfig { 14 | } 15 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/sms/DefaultSmsCodeSender.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate.sms; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | /** 6 | * @Author: HanLong 7 | * @Date: Create in 2018/3/24 11:10 8 | * @Description: 模拟短信发送 9 | */ 10 | public class DefaultSmsCodeSender implements SmsCodeSender { 11 | 12 | @Override 13 | public void send(String phone, String code) { 14 | System.out.println("向手机号:" + phone + ",发送验证码:" + code); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/dto/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.dto; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/18 14:59 6 | * @Description: 7 | */ 8 | public class BaseResponse { 9 | 10 | public BaseResponse(Object content) { 11 | this.content = content; 12 | } 13 | 14 | private Object content; 15 | 16 | public Object getContent() { 17 | return content; 18 | } 19 | 20 | public void setContent(Object content) { 21 | this.content = content; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/service/impl/HelloServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.service.impl; 2 | 3 | import com.whyalwaysmea.service.HelloService; 4 | import org.springframework.stereotype.Service; 5 | 6 | /** 7 | * @Author: HanLong 8 | * @Date: Create in 2018/3/16 20:51 9 | * @Description: 10 | */ 11 | @Service 12 | public class HelloServiceImpl implements HelloService { 13 | 14 | @Override 15 | public String greeting(String name) { 16 | System.out.println("greeting"); 17 | return "hello "+name; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/authentication/AuthorizeConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.authentication; 2 | 3 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 4 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 5 | 6 | /** 7 | * @Author: HanLong 8 | * @Date: Create in 2018/3/31 15:02 9 | * @Description: 10 | */ 11 | public interface AuthorizeConfigProvider { 12 | 13 | void config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config); 14 | } 15 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/authentication/AuthorizeConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.authentication; 2 | 3 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 4 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 5 | 6 | /** 7 | * @Author: HanLong 8 | * @Date: Create in 2018/3/31 15:05 9 | * @Description: 10 | */ 11 | public interface AuthorizeConfigManager { 12 | 13 | void config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/exception/UserNotExistException.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.exception; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/16 20:48 6 | * @Description: 7 | */ 8 | public class UserNotExistException extends RuntimeException { 9 | 10 | private static final long serialVersionUID = -6112780192479692859L; 11 | 12 | private String id; 13 | 14 | public UserNotExistException(String id) { 15 | super("user not exist"); 16 | this.id = id; 17 | } 18 | 19 | public String getId() { 20 | return id; 21 | } 22 | 23 | public void setId(String id) { 24 | this.id = id; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/code/ImageCodeGenerator.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.code; 2 | 3 | import com.whyalwaysmea.core.validate.image.ImageCode; 4 | import com.whyalwaysmea.core.validate.ValidateCodeGenerator; 5 | import org.springframework.web.context.request.ServletWebRequest; 6 | 7 | /** 8 | * @Author: HanLong 9 | * @Date: Create in 2018/3/24 10:11 10 | * @Description: 11 | */ 12 | //@Component("imageCodeGenerator") 13 | public class ImageCodeGenerator implements ValidateCodeGenerator{ 14 | 15 | 16 | @Override 17 | public ImageCode generate(ServletWebRequest request) { 18 | System.out.println("自定义的图形验证码生成器"); 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring-Security 2 | Spring Boot + Spring Security 3 | 4 | 1. [SpringBoot + Spring Security 基本使用及个性化登录配置](http://blog.csdn.net/u013435893/article/details/79596628) 5 | 2. [SpringBoot + SpringSecurity 实现图形验证码功能](http://blog.csdn.net/u013435893/article/details/79617872) 6 | 3. [SpringBoot + SpringSecurity “记住我”功能实现及相关源码分析](https://blog.csdn.net/u013435893/article/details/79675548) 7 | 4. [SpringBoot + SpringSecurity 短信验证码登录功能](https://blog.csdn.net/u013435893/article/details/79684027) 8 | 5. [SpringBoot + SpringSecurity OAuth2.0 简单使用](https://blog.csdn.net/u013435893/article/details/79735097)  9 | 6. [SpringBoot + SpringSecurity 控制授权](https://blog.csdn.net/u013435893/article/details/79770052) 10 | -------------------------------------------------------------------------------- /security-browser/src/main/resources/resources/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录页面 6 | 7 | 8 |

自定义登录页面

9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
用户名:
密码:
23 |
24 | 25 | -------------------------------------------------------------------------------- /security-demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.driver-class-name = com.mysql.jdbc.Driver 2 | spring.datasource.url= jdbc:mysql://127.0.0.1:3306/security?useUnicode=yes&characterEncoding=UTF-8&useSSL=false 3 | spring.datasource.username = root 4 | spring.datasource.password = root 5 | 6 | server.port=8082 7 | 8 | spring.session.store-type = REDIS 9 | 10 | # security 使能 11 | #security.basic.enabled = false 12 | 13 | server.session.timeout = 600 14 | 15 | 16 | # web端登录界面 17 | my.security.browser.loginPage = /demo-login.html 18 | # 登录成功后的处理方式(跳转,返回json) 19 | #my.security.browser.loginType = REDIRECT 20 | 21 | 22 | # 图形验证码配置 23 | #my.security.code.image.length = 60 24 | #my.security.code.image.width = 100 25 | #my.security.code.image.url = /user, /user/* -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/properties/ValidateCodeProperties.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.properties; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/21 22:08 6 | * @Description: 验证码相关的配置 7 | */ 8 | public class ValidateCodeProperties { 9 | 10 | private ImageProperties image = new ImageProperties(); 11 | 12 | private SmsCodeProperties sms = new SmsCodeProperties(); 13 | 14 | public ImageProperties getImage() { 15 | return image; 16 | } 17 | 18 | public void setImage(ImageProperties image) { 19 | this.image = image; 20 | } 21 | 22 | public SmsCodeProperties getSms() { 23 | return sms; 24 | } 25 | 26 | public void setSms(SmsCodeProperties sms) { 27 | this.sms = sms; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/validator/MyConstraint.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.validator; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @Author: HanLong 12 | * @Date: Create in 2018/3/16 20:50 13 | * @Description: 14 | */ 15 | @Target({ElementType.METHOD, ElementType.FIELD}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Constraint(validatedBy = MyConstraintValidator.class) 18 | public @interface MyConstraint { 19 | 20 | String message(); 21 | 22 | Class[] groups() default { }; 23 | 24 | Class[] payload() default { }; 25 | 26 | } -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/validator/ValidateException.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.validator; 2 | 3 | import org.springframework.validation.ObjectError; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @Author: HanLong 9 | * @Date: Create in 2018/3/16 20:47 10 | * @Description: 11 | */ 12 | public class ValidateException extends RuntimeException { 13 | 14 | private static final long serialVersionUID = 7207451175263593487L; 15 | 16 | private List errors; 17 | 18 | public ValidateException(List errors) { 19 | this.errors = errors; 20 | } 21 | 22 | public List getErrors() { 23 | return errors; 24 | } 25 | 26 | public void setErrors(List errors) { 27 | this.errors = errors; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /security-app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | security 7 | com.whyalwaysmea.security 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | security-app 13 | 14 | 15 | 16 | com.whyalwaysmea.security 17 | security-core 18 | ${my.security.version} 19 | 20 | 21 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeProcessor.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import org.springframework.web.context.request.ServletWebRequest; 4 | 5 | /** 6 | * @Author: HanLong 7 | * @Date: Create in 2018/3/24 12:47 8 | * @Description: 校验码处理器,封装不同校验码的处理逻辑 9 | */ 10 | public interface ValidateCodeProcessor { 11 | 12 | /** 13 | * 验证码放入session时的前缀 14 | */ 15 | String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_"; 16 | 17 | /** 18 | * 创建校验码 19 | * 20 | * @param request 21 | * @throws Exception 22 | */ 23 | void create(ServletWebRequest request) throws Exception; 24 | 25 | /** 26 | * 校验验证码 27 | * 28 | * @param servletWebRequest 29 | * @throws Exception 30 | */ 31 | void validate(ServletWebRequest servletWebRequest); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/SecurityCoreConfig.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core; 2 | 3 | import com.whyalwaysmea.core.properties.SecurityProperties; 4 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | 10 | /** 11 | * @Author: HanLong 12 | * @Date: Create in 2018/3/18 15:32 13 | * @Description: 14 | */ 15 | @Configuration 16 | @EnableConfigurationProperties(SecurityProperties.class) 17 | public class SecurityCoreConfig { 18 | 19 | @Bean 20 | public PasswordEncoder passwordEncoder() { 21 | return new BCryptPasswordEncoder(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/image/ImageCodeProcessor.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate.image; 2 | 3 | import com.whyalwaysmea.core.validate.impl.AbstractValidateCodeProcessor; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.web.context.request.ServletWebRequest; 6 | 7 | import javax.imageio.ImageIO; 8 | 9 | /** 10 | * @Author: HanLong 11 | * @Date: Create in 2018/3/24 14:26 12 | * @Description: 13 | */ 14 | @Component("imageValidateCodeProcessor") 15 | public class ImageCodeProcessor extends AbstractValidateCodeProcessor { 16 | 17 | /** 18 | * 发送图形验证码,将其写到响应中 19 | */ 20 | @Override 21 | protected void send(ServletWebRequest request, ImageCode imageCode) throws Exception { 22 | ImageIO.write(imageCode.getImage(), "JPEG", request.getResponse().getOutputStream()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/properties/ImageProperties.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.properties; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/21 22:07 6 | * @Description: 图片验证码配置 7 | */ 8 | public class ImageProperties extends SmsCodeProperties{ 9 | 10 | /** 11 | * 图片验证码默认长度为4 12 | */ 13 | public ImageProperties() {setLength(4);} 14 | 15 | /** 16 | * 图片的宽 17 | */ 18 | private int width = 60; 19 | 20 | /** 21 | * 图片的高 22 | */ 23 | private int height = 40; 24 | 25 | public int getWidth() { 26 | return width; 27 | } 28 | 29 | public void setWidth(int width) { 30 | this.width = width; 31 | } 32 | 33 | public int getHeight() { 34 | return height; 35 | } 36 | 37 | public void setHeight(int height) { 38 | this.height = height; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/dto/UserQueryCondition.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.dto; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | 5 | /** 6 | * @Author: HanLong 7 | * @Date: Create in 2018/3/9 22:16 8 | * @Description: 9 | */ 10 | public class UserQueryCondition { 11 | 12 | private String username; 13 | 14 | private int age; 15 | 16 | private int ageTo; 17 | 18 | public String getUsername() { 19 | return username; 20 | } 21 | 22 | public void setUsername(String username) { 23 | this.username = username; 24 | } 25 | 26 | public int getAge() { 27 | return age; 28 | } 29 | 30 | public void setAge(int age) { 31 | this.age = age; 32 | } 33 | 34 | public int getAgeTo() { 35 | return ageTo; 36 | } 37 | 38 | public void setAgeTo(int ageTo) { 39 | this.ageTo = ageTo; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeType.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import com.whyalwaysmea.core.properties.SecurityConstants; 4 | 5 | /** 6 | * @Author: HanLong 7 | * @Date: Create in 2018/3/24 12:48 8 | * @Description: 9 | */ 10 | public enum ValidateCodeType { 11 | 12 | /** 13 | * 短信验证码 14 | */ 15 | SMS { 16 | @Override 17 | public String getParamNameOnValidate() { 18 | return SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_SMS; 19 | } 20 | }, 21 | /** 22 | * 图片验证码 23 | */ 24 | IMAGE { 25 | @Override 26 | public String getParamNameOnValidate() { 27 | return SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_IMAGE; 28 | } 29 | }; 30 | 31 | /** 32 | * 校验时从请求中获取的参数的名字 33 | * @return 34 | */ 35 | public abstract String getParamNameOnValidate(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/properties/SmsCodeProperties.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.properties; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/24 14:16 6 | * @Description: 验证码 7 | */ 8 | public class SmsCodeProperties { 9 | 10 | /** 11 | * 验证码长度 12 | */ 13 | private int length = 6; 14 | 15 | /** 16 | * 过期时间 17 | */ 18 | private int expireIn = 60; 19 | 20 | private String url; 21 | 22 | public int getLength() { 23 | return length; 24 | } 25 | public void setLength(int lenght) { 26 | this.length = lenght; 27 | } 28 | public int getExpireIn() { 29 | return expireIn; 30 | } 31 | public void setExpireIn(int expireIn) { 32 | this.expireIn = expireIn; 33 | } 34 | public String getUrl() { 35 | return url; 36 | } 37 | public void setUrl(String url) { 38 | this.url = url; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/properties/SecurityProperties.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * @Author: HanLong 7 | * @Date: Create in 2018/3/18 15:20 8 | * @Description: 可配置的属性 9 | */ 10 | @ConfigurationProperties(prefix = "my.security") 11 | public class SecurityProperties { 12 | 13 | private BrowserProperties browser = new BrowserProperties(); 14 | 15 | private ValidateCodeProperties code = new ValidateCodeProperties(); 16 | 17 | public BrowserProperties getBrowser() { 18 | return browser; 19 | } 20 | 21 | public void setBrowser(BrowserProperties browser) { 22 | this.browser = browser; 23 | } 24 | 25 | public ValidateCodeProperties getCode() { 26 | return code; 27 | } 28 | 29 | public void setCode(ValidateCodeProperties code) { 30 | this.code = code; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /security-browser/src/main/java/com/whyalwaysmea/browser/session/MyInvalidSessionStrategy.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.browser.session; 2 | 3 | import org.springframework.security.web.session.InvalidSessionStrategy; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import java.io.IOException; 9 | 10 | /** 11 | * @Author: HanLong 12 | * @Date: Create in 2018/3/27 21:44 13 | * @Description: Session并发失效 14 | */ 15 | public class MyInvalidSessionStrategy extends AbstractSessionStrategy implements InvalidSessionStrategy { 16 | 17 | public MyInvalidSessionStrategy(String invalidSessionUrl) { 18 | super(invalidSessionUrl); 19 | } 20 | 21 | @Override 22 | public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) 23 | throws IOException, ServletException { 24 | onSessionInvalid(request, response); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/validator/MyConstraintValidator.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.validator; 2 | 3 | import com.whyalwaysmea.service.HelloService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | 9 | /** 10 | * @Author: HanLong 11 | * @Date: Create in 2018/3/16 20:50 12 | * @Description: 13 | */ 14 | public class MyConstraintValidator implements ConstraintValidator { 15 | 16 | @Autowired 17 | private HelloService helloService; 18 | 19 | @Override 20 | public void initialize(MyConstraint constraintAnnotation) { 21 | System.out.println("my validator init"); 22 | } 23 | 24 | @Override 25 | public boolean isValid(Object value, ConstraintValidatorContext context) { 26 | helloService.greeting("tom"); 27 | System.out.println(value); 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea; 2 | 3 | 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.ComponentScans; 9 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | /** 14 | * @Author: HanLong 15 | * @Date: Create in 2018/2/1 21:45 16 | * @Description: 17 | */ 18 | @SpringBootApplication 19 | @ComponentScan(basePackages = "com.whyalwaysmea") 20 | @EnableAspectJAutoProxy 21 | public class DemoApplication { 22 | 23 | /** 24 | * @param args 25 | */ 26 | public static void main(String[] args) { 27 | SpringApplication.run(DemoApplication.class, args); 28 | } 29 | 30 | 31 | } -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/dto/User.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.dto; 2 | 3 | import com.whyalwaysmea.validator.MyConstraint; 4 | import org.hibernate.validator.constraints.NotBlank; 5 | 6 | /** 7 | * @Author: HanLong 8 | * @Date: Create in 2018/3/8 21:40 9 | * @Description: 10 | */ 11 | public class User { 12 | 13 | @MyConstraint(message = "自定义的校验器") 14 | private String userName; 15 | 16 | @NotBlank 17 | private String password; 18 | 19 | public User() { 20 | 21 | } 22 | 23 | public User(String userName, String password) { 24 | this.userName = userName; 25 | this.password = password; 26 | } 27 | 28 | public String getUserName() { 29 | return userName; 30 | } 31 | 32 | public void setUserName(String userName) { 33 | this.userName = userName; 34 | } 35 | 36 | public String getPassword() { 37 | return password; 38 | } 39 | 40 | public void setPassword(String password) { 41 | this.password = password; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/image/ImageCode.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate.image; 2 | 3 | import com.whyalwaysmea.core.validate.ValidateCode; 4 | import org.apache.commons.lang.Validate; 5 | 6 | import java.awt.image.BufferedImage; 7 | import java.time.LocalDateTime; 8 | 9 | /** 10 | * @Author: HanLong 11 | * @Date: Create in 2018/3/19 21:09 12 | * @Description: 13 | */ 14 | public class ImageCode extends ValidateCode { 15 | 16 | private BufferedImage image; 17 | 18 | public ImageCode(BufferedImage image, String code, int expireId) { 19 | super(code, LocalDateTime.now().plusSeconds(expireId)); 20 | this.image = image; 21 | } 22 | 23 | public ImageCode(BufferedImage image, String code, LocalDateTime localDateTime) { 24 | super(code, localDateTime); 25 | this.image = image; 26 | } 27 | 28 | 29 | public BufferedImage getImage() { 30 | return image; 31 | } 32 | 33 | public void setImage(BufferedImage image) { 34 | this.image = image; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/security/DemoAuthorizeConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.security; 2 | 3 | import com.whyalwaysmea.core.authentication.AuthorizeConfigProvider; 4 | import org.springframework.core.annotation.Order; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @Author: HanLong 11 | * @Date: Create in 2018/3/31 15:09 12 | * @Description: 跟业务相关的权限配置 13 | */ 14 | @Component 15 | @Order(Integer.MAX_VALUE) 16 | public class DemoAuthorizeConfigProvider implements AuthorizeConfigProvider { 17 | 18 | 19 | @Override 20 | public void config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config) { 21 | // config.antMatchers("/user") 22 | // .hasRole("ADMIN"); 23 | 24 | config.anyRequest().access("@rbacPermission.hasPermission(request, authentication)"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /security-browser/src/main/java/com/whyalwaysmea/browser/session/MyExpiredSessionStrategy.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.browser.session; 2 | 3 | 4 | import java.io.IOException; 5 | 6 | import javax.servlet.ServletException; 7 | 8 | import org.springframework.security.web.session.SessionInformationExpiredEvent; 9 | import org.springframework.security.web.session.SessionInformationExpiredStrategy; 10 | 11 | /** 12 | * @Author: HanLong 13 | * @Date: Create in 2018/3/27 21:41 14 | * @Description: Session超时失效 15 | */ 16 | public class MyExpiredSessionStrategy extends AbstractSessionStrategy implements SessionInformationExpiredStrategy { 17 | 18 | public MyExpiredSessionStrategy(String invalidSessionUrl) { 19 | super(invalidSessionUrl); 20 | } 21 | 22 | @Override 23 | public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { 24 | onSessionInvalid(event.getRequest(), event.getResponse()); 25 | } 26 | 27 | @Override 28 | protected boolean isConcurrency() { 29 | return true; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeProcessorHolder.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * @Author: HanLong 10 | * @Date: Create in 2018/3/24 12:48 11 | * @Description: 12 | */ 13 | @Component 14 | public class ValidateCodeProcessorHolder { 15 | 16 | @Autowired 17 | private Map validateCodeProcessors; 18 | 19 | public ValidateCodeProcessor findValidateCodeProcessor(ValidateCodeType type) { 20 | return findValidateCodeProcessor(type.toString().toLowerCase()); 21 | } 22 | 23 | public ValidateCodeProcessor findValidateCodeProcessor(String type) { 24 | String name = type.toLowerCase() + ValidateCodeProcessor.class.getSimpleName(); 25 | ValidateCodeProcessor processor = validateCodeProcessors.get(name); 26 | if (processor == null) { 27 | throw new ValidateCodeException("验证码处理器" + name + "不存在"); 28 | } 29 | return processor; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/authentication/MyAuthorizeConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.authentication; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | /** 12 | * @Author: HanLong 13 | * @Date: Create in 2018/3/31 15:06 14 | * @Description: 搜集所有的AuthorizeConfigProvider 15 | */ 16 | @Component 17 | public class MyAuthorizeConfigManager implements AuthorizeConfigManager{ 18 | 19 | @Autowired 20 | private List authorizeConfigProviders; 21 | 22 | @Override 23 | public void config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config) { 24 | for (AuthorizeConfigProvider authorizeConfigProvider : authorizeConfigProviders) { 25 | authorizeConfigProvider.config(config); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/web/controller/AsyncController.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.web.controller; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | import java.util.concurrent.Callable; 9 | 10 | /** 11 | * @Author: HanLong 12 | * @Date: Create in 2018/3/16 21:33 13 | * @Description: 14 | */ 15 | @RestController 16 | public class AsyncController { 17 | 18 | private Logger logger = LoggerFactory.getLogger(getClass()); 19 | 20 | @GetMapping("/test") 21 | public Callable test() throws InterruptedException { 22 | logger.info("进入了主线程"); 23 | 24 | Callable callable = new Callable() { 25 | @Override 26 | public String call() throws Exception { 27 | logger.info("进入了子线程"); 28 | Thread.sleep(5000); 29 | logger.info("结束了子线程"); 30 | return "success"; 31 | } 32 | }; 33 | logger.info("主线程结束"); 34 | return callable; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/web/controller/ControllerExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.web.controller; 2 | 3 | import com.whyalwaysmea.exception.UserNotExistException; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ControllerAdvice; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | import org.springframework.web.bind.annotation.ResponseStatus; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * @Author: HanLong 15 | * @Date: Create in 2018/3/16 20:49 16 | * @Description: 17 | */ 18 | @ControllerAdvice 19 | public class ControllerExceptionHandler { 20 | 21 | @ExceptionHandler(UserNotExistException.class) 22 | @ResponseBody 23 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 24 | public Map handleUserNotExistException(UserNotExistException ex) { 25 | Map result = new HashMap<>(); 26 | result.put("id", ex.getId()); 27 | result.put("message", ex.getMessage()); 28 | return result; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.config.annotation.SecurityConfigurerAdapter; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.web.DefaultSecurityFilterChain; 7 | import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; 8 | import org.springframework.stereotype.Component; 9 | import javax.servlet.Filter; 10 | 11 | /** 12 | * @Author: HanLong 13 | * @Date: Create in 2018/3/24 15:54 14 | * @Description: 15 | */ 16 | @Component("validateCodeSecurityConfig") 17 | public class ValidateCodeSecurityConfig extends SecurityConfigurerAdapter { 18 | 19 | @Autowired 20 | private Filter validateCodeFilter; 21 | 22 | @Override 23 | public void configure(HttpSecurity http) throws Exception { 24 | http.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/web/filter/TimeFilter.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.web.filter; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import javax.servlet.*; 6 | import java.io.IOException; 7 | import java.util.Date; 8 | 9 | /** 10 | * @Author: HanLong 11 | * @Date: Create in 2018/3/16 21:02 12 | * @Description: 13 | */ 14 | //@Component 15 | public class TimeFilter implements Filter { 16 | 17 | @Override 18 | public void destroy() { 19 | System.out.println("time filter destroy"); 20 | } 21 | 22 | @Override 23 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 24 | throws IOException, ServletException { 25 | System.out.println("time filter start"); 26 | long start = new Date().getTime(); 27 | chain.doFilter(request, response); 28 | System.out.println("time filter 耗时:"+ (new Date().getTime() - start)); 29 | System.out.println("time filter finish"); 30 | } 31 | 32 | @Override 33 | public void init(FilterConfig arg0) throws ServletException { 34 | System.out.println("time filter init"); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/web/aspect/TimeAspect.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.web.aspect; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | import org.aspectj.lang.annotation.Around; 5 | import org.aspectj.lang.annotation.Aspect; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @Author: HanLong 12 | * @Date: Create in 2018/3/16 21:05 13 | * @Description: 14 | */ 15 | @Aspect 16 | @Component 17 | public class TimeAspect { 18 | 19 | @Around("execution(* com.whyalwaysmea.web.controller.UserController.*(..))") 20 | public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable { 21 | 22 | System.out.println("time aspect start"); 23 | 24 | Object[] args = pjp.getArgs(); 25 | for (Object arg : args) { 26 | System.out.println("arg is "+arg); 27 | } 28 | 29 | long start = new Date().getTime(); 30 | 31 | Object object = pjp.proceed(); 32 | 33 | System.out.println("time aspect 耗时:"+ (new Date().getTime() - start)); 34 | 35 | System.out.println("time aspect end"); 36 | 37 | return object; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /security-browser/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | security 7 | com.whyalwaysmea.security 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | security-browser 13 | 14 | 15 | 16 | com.whyalwaysmea.security 17 | security-core 18 | ${my.security.version} 19 | 20 | 21 | org.springframework.session 22 | spring-session 23 | 24 | 25 | org.apache.shiro 26 | shiro-core 27 | 1.2.2 28 | 29 | 30 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/validator/ValidateAspect.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.validator; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | import org.aspectj.lang.annotation.Around; 5 | import org.aspectj.lang.annotation.Aspect; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.validation.BindingResult; 8 | 9 | /** 10 | * @Author: HanLong 11 | * @Date: Create in 2018/3/16 20:55 12 | * @Description: 13 | */ 14 | @Aspect 15 | @Component 16 | public class ValidateAspect { 17 | 18 | @Around("execution(* com.whyalwaysmea.web.controller.UserController.*(..))") 19 | public Object handleValidateResult(ProceedingJoinPoint pjp) throws Throwable { 20 | 21 | System.out.println("进入切片"); 22 | 23 | Object[] args = pjp.getArgs(); 24 | for (Object arg : args) { 25 | if(arg instanceof BindingResult) { 26 | BindingResult errors = (BindingResult)arg; 27 | if (errors.hasErrors()) { 28 | throw new ValidateException(errors.getAllErrors()); 29 | } 30 | } 31 | } 32 | 33 | Object result = pjp.proceed(); 34 | 35 | return result; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCode.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | /** 6 | * @Author: HanLong 7 | * @Date: Create in 2018/3/24 11:06 8 | * @Description: 9 | */ 10 | public class ValidateCode { 11 | 12 | private String code; 13 | 14 | private LocalDateTime expireTime; 15 | 16 | public ValidateCode(String code, int expireIn){ 17 | this.code = code; 18 | this.expireTime = LocalDateTime.now().plusSeconds(expireIn); 19 | } 20 | 21 | public ValidateCode(String code, LocalDateTime expireTime) { 22 | this.code = code; 23 | this.expireTime = expireTime; 24 | } 25 | 26 | public String getCode() { 27 | return code; 28 | } 29 | 30 | public void setCode(String code) { 31 | this.code = code; 32 | } 33 | 34 | public LocalDateTime getExpireTime() { 35 | return expireTime; 36 | } 37 | 38 | public void setExpireTime(LocalDateTime expireTime) { 39 | this.expireTime = expireTime; 40 | } 41 | 42 | /** 43 | * 是否过期 44 | * @return 45 | */ 46 | public boolean isExpried() { 47 | return LocalDateTime.now().isAfter(getExpireTime()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/sms/SmsCodeProcessor.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate.sms; 2 | 3 | import com.whyalwaysmea.core.properties.SecurityConstants; 4 | import com.whyalwaysmea.core.validate.ValidateCode; 5 | import com.whyalwaysmea.core.validate.impl.AbstractValidateCodeProcessor; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.bind.ServletRequestUtils; 9 | import org.springframework.web.context.request.ServletWebRequest; 10 | 11 | /** 12 | * @Author: HanLong 13 | * @Date: Create in 2018/3/24 14:27 14 | * @Description: 15 | */ 16 | @Component("smsValidateCodeProcessor") 17 | public class SmsCodeProcessor extends AbstractValidateCodeProcessor { 18 | 19 | /** 20 | * 短信验证码发送器 21 | */ 22 | @Autowired 23 | private SmsCodeSender smsCodeSender; 24 | 25 | @Override 26 | protected void send(ServletWebRequest request, ValidateCode validateCode) throws Exception { 27 | String paramName = SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE; 28 | String mobile = ServletRequestUtils.getRequiredStringParameter(request.getRequest(), paramName); 29 | smsCodeSender.send(mobile, validateCode.getCode()); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/properties/SessionProperties.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.properties; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/27 21:45 6 | * @Description: Session相关配置 7 | */ 8 | public class SessionProperties { 9 | 10 | /** 11 | * 同一个用户在系统中的最大session数,默认1 12 | */ 13 | private int maximumSessions = 1; 14 | /** 15 | * 达到最大session时是否阻止新的登录请求,默认为false,不阻止,新的登录会将老的登录失效掉 16 | */ 17 | private boolean maxSessionsPreventsLogin; 18 | /** 19 | * session失效时跳转的地址 20 | */ 21 | private String sessionInvalidUrl = SecurityConstants.DEFAULT_SESSION_INVALID_URL; 22 | 23 | public int getMaximumSessions() { 24 | return maximumSessions; 25 | } 26 | 27 | public void setMaximumSessions(int maximumSessions) { 28 | this.maximumSessions = maximumSessions; 29 | } 30 | 31 | public boolean isMaxSessionsPreventsLogin() { 32 | return maxSessionsPreventsLogin; 33 | } 34 | 35 | public void setMaxSessionsPreventsLogin(boolean maxSessionsPreventsLogin) { 36 | this.maxSessionsPreventsLogin = maxSessionsPreventsLogin; 37 | } 38 | 39 | public String getSessionInvalidUrl() { 40 | return sessionInvalidUrl; 41 | } 42 | 43 | public void setSessionInvalidUrl(String sessionInvalidUrl) { 44 | this.sessionInvalidUrl = sessionInvalidUrl; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/properties/SecurityConstants.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.properties; 2 | 3 | /** 4 | * @Author: HanLong 5 | * @Date: Create in 2018/3/18 15:22 6 | * @Description: 7 | */ 8 | public interface SecurityConstants { 9 | 10 | /** 11 | * 默认登录页面 12 | */ 13 | String DEFAULT_LOGIN_PAGE_URL = "/login.html"; 14 | 15 | /** 16 | * 当请求需要身份认证时,默认跳转的url 17 | */ 18 | String DEFAULT_UNAUTHENTICATION_URL = "/authentication/require"; 19 | 20 | /** 21 | * 默认的用户名密码登录请求处理url 22 | */ 23 | String DEFAULT_LOGIN_PROCESSING_URL_FORM = "/authentication/form"; 24 | /** 25 | * 默认的手机验证码登录请求处理url 26 | */ 27 | String DEFAULT_LOGIN_PROCESSING_URL_MOBILE = "/authentication/mobile"; 28 | 29 | /** 30 | * 默认的处理验证码的url前缀 31 | */ 32 | String DEFAULT_VALIDATE_CODE_URL_PREFIX = "/code"; 33 | 34 | /** 35 | * 验证图片验证码时,http请求中默认的携带图片验证码信息的参数的名称 36 | */ 37 | String DEFAULT_PARAMETER_NAME_CODE_IMAGE = "imageCode"; 38 | 39 | /** 40 | * 验证短信验证码时,http请求中默认的携带短信验证码信息的参数的名称 41 | */ 42 | String DEFAULT_PARAMETER_NAME_CODE_SMS = "smsCode"; 43 | 44 | /** 45 | * 发送短信验证码 或 验证短信验证码时,传递手机号的参数的名称 46 | */ 47 | String DEFAULT_PARAMETER_NAME_MOBILE = "mobile"; 48 | 49 | /** 50 | * session失效默认的跳转地址 51 | */ 52 | String DEFAULT_SESSION_INVALID_URL = "/session/invalid"; 53 | } 54 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/sms/SmsCodeGenerator.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate.sms; 2 | 3 | import com.whyalwaysmea.core.properties.SecurityProperties; 4 | import com.whyalwaysmea.core.validate.ValidateCode; 5 | import com.whyalwaysmea.core.validate.ValidateCodeGenerator; 6 | import org.apache.commons.lang.RandomStringUtils; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.context.request.ServletWebRequest; 10 | 11 | /** 12 | * @Author: HanLong 13 | * @Date: Create in 2018/3/24 14:26 14 | * @Description: 短信验证码生成器 15 | */ 16 | @Component("smsValidateCodeGenerator") 17 | public class SmsCodeGenerator implements ValidateCodeGenerator { 18 | 19 | @Autowired 20 | private SecurityProperties securityProperties; 21 | 22 | @Override 23 | public ValidateCode generate(ServletWebRequest request) { 24 | String code = RandomStringUtils.randomNumeric(securityProperties.getCode().getSms().getLength()); 25 | return new ValidateCode(code, securityProperties.getCode().getSms().getExpireIn()); 26 | } 27 | 28 | public SecurityProperties getSecurityProperties() { 29 | return securityProperties; 30 | } 31 | 32 | public void setSecurityProperties(SecurityProperties securityProperties) { 33 | this.securityProperties = securityProperties; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/authentication/MyAuthorizeConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.authentication; 2 | 3 | import com.whyalwaysmea.core.properties.SecurityConstants; 4 | import com.whyalwaysmea.core.properties.SecurityProperties; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.core.annotation.Order; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * @Author: HanLong 13 | * @Date: Create in 2018/3/31 15:03 14 | * @Description: 默认的权限配置 15 | */ 16 | @Component 17 | @Order(Integer.MIN_VALUE) 18 | public class MyAuthorizeConfigProvider implements AuthorizeConfigProvider { 19 | 20 | @Autowired 21 | private SecurityProperties securityProperties; 22 | 23 | 24 | @Override 25 | public void config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config) { 26 | config.antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL, 27 | SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, 28 | securityProperties.getBrowser().getLoginPage(), 29 | SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*", 30 | SecurityConstants.DEFAULT_SESSION_INVALID_URL) 31 | .permitAll(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/security/RbacPermission.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.security; 2 | 3 | import org.springframework.core.annotation.Order; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.util.AntPathMatcher; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * @Author: HanLong 15 | * @Date: Create in 2018/3/31 16:04 16 | * @Description: 跟业务相关的权限配置 17 | */ 18 | @Component("rbacPermission") 19 | public class RbacPermission { 20 | 21 | private AntPathMatcher antPathMatcher = new AntPathMatcher(); 22 | 23 | public boolean hasPermission(HttpServletRequest request, Authentication authentication) { 24 | Object principal = authentication.getPrincipal(); 25 | boolean hasPermission = false; 26 | if(principal instanceof UserDetails) { 27 | String username = ((UserDetails) principal).getUsername(); 28 | // 读取用户所拥有的权限url 29 | Set urls = new HashSet<>(); 30 | urls.add("/user"); 31 | for (String url : urls) { 32 | if(antPathMatcher.match(url, request.getRequestURI())) { 33 | hasPermission = true; 34 | break; 35 | } 36 | } 37 | 38 | } 39 | return hasPermission; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /security-browser/src/main/java/com/whyalwaysmea/browser/config/BrowserSecurityBeanConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.whyalwaysmea.browser.config; 5 | 6 | import com.whyalwaysmea.browser.session.MyExpiredSessionStrategy; 7 | import com.whyalwaysmea.browser.session.MyInvalidSessionStrategy; 8 | import com.whyalwaysmea.core.properties.SecurityProperties; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.security.web.session.InvalidSessionStrategy; 14 | import org.springframework.security.web.session.SessionInformationExpiredStrategy; 15 | 16 | 17 | /** 18 | * @author zhailiang 19 | * 20 | */ 21 | @Configuration 22 | public class BrowserSecurityBeanConfig { 23 | 24 | @Autowired 25 | private SecurityProperties securityProperties; 26 | 27 | @Bean 28 | @ConditionalOnMissingBean(InvalidSessionStrategy.class) 29 | public InvalidSessionStrategy invalidSessionStrategy(){ 30 | return new MyInvalidSessionStrategy(securityProperties.getBrowser().getSession().getSessionInvalidUrl()); 31 | } 32 | 33 | @Bean 34 | @ConditionalOnMissingBean(SessionInformationExpiredStrategy.class) 35 | public SessionInformationExpiredStrategy sessionInformationExpiredStrategy(){ 36 | return new MyExpiredSessionStrategy(securityProperties.getBrowser().getSession().getSessionInvalidUrl()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/properties/BrowserProperties.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.properties; 2 | 3 | 4 | /** 5 | * @Author: HanLong 6 | * @Date: Create in 2018/3/18 15:21 7 | * @Description: 浏览器登录认证相关配置 8 | */ 9 | public class BrowserProperties { 10 | 11 | private SessionProperties session = new SessionProperties(); 12 | 13 | /** 14 | * 默认登录页面 15 | */ 16 | private String loginPage = SecurityConstants.DEFAULT_LOGIN_PAGE_URL; 17 | 18 | /** 19 | * 默认登录方式 20 | */ 21 | private LoginResponseType loginType = LoginResponseType.JSON; 22 | 23 | /** 24 | * 默认记住我的时长 25 | */ 26 | private int rememberMeSeconds = 3600; 27 | 28 | public SessionProperties getSession() { 29 | return session; 30 | } 31 | 32 | public void setSession(SessionProperties session) { 33 | this.session = session; 34 | } 35 | 36 | public String getLoginPage() { 37 | return loginPage; 38 | } 39 | 40 | public void setLoginPage(String loginPage) { 41 | this.loginPage = loginPage; 42 | } 43 | 44 | public LoginResponseType getLoginType() { 45 | return loginType; 46 | } 47 | 48 | public void setLoginType(LoginResponseType loginType) { 49 | this.loginType = loginType; 50 | } 51 | 52 | public int getRememberMeSeconds() { 53 | return rememberMeSeconds; 54 | } 55 | 56 | public void setRememberMeSeconds(int rememberMeSeconds) { 57 | this.rememberMeSeconds = rememberMeSeconds; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/security/MyUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.security; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.core.authority.AuthorityUtils; 7 | import org.springframework.security.core.userdetails.User; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.stereotype.Component; 13 | 14 | /** 15 | * @Author: HanLong 16 | * @Date: Create in 2018/3/17 22:33 17 | * @Description: 18 | */ 19 | @Component 20 | public class MyUserDetailsService implements UserDetailsService { 21 | 22 | 23 | private Logger logger = LoggerFactory.getLogger(getClass()); 24 | 25 | @Autowired 26 | private PasswordEncoder passwordEncoder; 27 | 28 | @Override 29 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 30 | logger.info("用户的用户名: {}", username); 31 | 32 | String password = passwordEncoder.encode("123456"); 33 | logger.info("password: {}", password); 34 | 35 | // 参数分别是:用户名,密码,用户权限 36 | User user = new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMI")); 37 | return user; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/web/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.web.config; 2 | 3 | import com.whyalwaysmea.web.filter.TimeFilter; 4 | import com.whyalwaysmea.web.interceptor.TimeInterceptor; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 10 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * @Author: HanLong 17 | * @Date: Create in 2018/3/16 21:03 18 | * @Description: 19 | */ 20 | //@Configuration 21 | public class WebConfig extends WebMvcConfigurerAdapter { 22 | 23 | @Autowired 24 | private TimeInterceptor timeInterceptor; 25 | 26 | @Override 27 | public void addInterceptors(InterceptorRegistry registry) { 28 | registry.addInterceptor(timeInterceptor); 29 | } 30 | 31 | 32 | @Bean 33 | public FilterRegistrationBean timeFilter() { 34 | 35 | FilterRegistrationBean registrationBean = new FilterRegistrationBean(); 36 | 37 | TimeFilter timeFilter = new TimeFilter(); 38 | registrationBean.setFilter(timeFilter); 39 | 40 | List urls = new ArrayList<>(); 41 | urls.add("/*"); 42 | registrationBean.setUrlPatterns(urls); 43 | 44 | return registrationBean; 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/authentication/sms/AbstractChannelSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.authentication.sms; 2 | 3 | import com.whyalwaysmea.core.properties.SecurityConstants; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 7 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 8 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 9 | 10 | /** 11 | * @Author: HanLong 12 | * @Date: Create in 2018/3/24 15:55 13 | * @Description: 基础的登录配置 14 | */ 15 | public class AbstractChannelSecurityConfig extends WebSecurityConfigurerAdapter { 16 | 17 | @Autowired 18 | protected AuthenticationSuccessHandler myAuthenticationSuccessHandler; 19 | 20 | @Autowired 21 | protected AuthenticationFailureHandler myAuthenticationFailureHandler; 22 | 23 | protected void applyPasswordAuthenticationConfig(HttpSecurity http) throws Exception { 24 | http.formLogin() 25 | .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) // 登录页面回调 26 | .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM) // 自定义的登录接口 27 | .successHandler(myAuthenticationSuccessHandler) // 认证成功回调 28 | .failureHandler(myAuthenticationFailureHandler); // 认证失败回调 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeBeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import com.whyalwaysmea.core.properties.SecurityProperties; 4 | import com.whyalwaysmea.core.validate.image.ImageValidateCodeGenerator; 5 | import com.whyalwaysmea.core.validate.sms.DefaultSmsCodeSender; 6 | import com.whyalwaysmea.core.validate.sms.SmsCodeSender; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | /** 13 | * @Author: HanLong 14 | * @Date: Create in 2018/3/24 9:54 15 | * @Description: 16 | */ 17 | @Configuration 18 | public class ValidateCodeBeanConfig { 19 | 20 | @Autowired 21 | private SecurityProperties securityProperties; 22 | 23 | /** 24 | * @ConditionalOnMissingBean 初始化的时候,先是容器中找有没有imageCodeGenerator,如果没有再用这个 25 | * @return 26 | */ 27 | @Bean 28 | @ConditionalOnMissingBean(name = "imageValidateCodeGenerator") 29 | public ValidateCodeGenerator imageValidateCodeGenerator() { 30 | ImageValidateCodeGenerator imageValidateCodeGenerator = new ImageValidateCodeGenerator(); 31 | imageValidateCodeGenerator.setSecurityProperties(securityProperties); 32 | return imageValidateCodeGenerator; 33 | } 34 | 35 | @Bean 36 | @ConditionalOnMissingBean(SmsCodeSender.class) 37 | public SmsCodeSender smsCodeGenerator() { 38 | SmsCodeSender smsCodeSender = new DefaultSmsCodeSender(); 39 | return smsCodeSender; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /security-demo/src/main/resources/resources/demo-login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录页面 6 | 7 | 8 |

DEMO登录页

9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 |
用户名:
密码:
图形验证码 22 | 23 | 24 |
记住我
31 | 32 |
35 |
36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 49 | 50 | 51 | 52 | 53 |
手机号:
短信验证码: 46 | 47 | 发送验证码 48 |
54 |
55 | 56 | -------------------------------------------------------------------------------- /security-app/src/main/java/com/whyalwaysmea/app/MyAuthorizationServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.app; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.authentication.AuthenticationManager; 6 | import org.springframework.security.core.userdetails.UserDetailsService; 7 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 8 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 9 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 10 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 11 | 12 | /** 13 | * @Author: HanLong 14 | * @Date: Create in 2018/3/27 21:58 15 | * @Description: 认证服务 16 | */ 17 | @Configuration 18 | @EnableAuthorizationServer 19 | public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 20 | 21 | @Autowired 22 | private AuthenticationManager authenticationManager; 23 | 24 | @Autowired 25 | private UserDetailsService userDetailsService; 26 | 27 | @Override 28 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 29 | endpoints.authenticationManager(authenticationManager) 30 | .userDetailsService(userDetailsService); 31 | } 32 | 33 | @Override 34 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 35 | clients.inMemory().withClient("haha") 36 | .secret("heihei") 37 | .accessTokenValiditySeconds(7200) 38 | .authorizedGrantTypes("refresh_token", "password") 39 | .scopes("all"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/authentication/sms/SmsCodeAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.authentication.sms; 2 | 3 | import org.springframework.security.authentication.AbstractAuthenticationToken; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.SpringSecurityCoreVersion; 6 | 7 | import java.util.Collection; 8 | 9 | /** 10 | * @Author: HanLong 11 | * @Date: Create in 2018/3/24 15:29 12 | * @Description: 手机验证码登录信息 类似于{@link UsernamePasswordAuthenticationToken} 13 | */ 14 | public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken { 15 | 16 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 17 | 18 | /** 19 | * 手机号 20 | */ 21 | private final Object principal; 22 | 23 | public SmsCodeAuthenticationToken(String mobile) { 24 | super(null); 25 | this.principal = mobile; 26 | setAuthenticated(false); 27 | } 28 | 29 | public SmsCodeAuthenticationToken(Object principal, 30 | Collection authorities) { 31 | super(authorities); 32 | this.principal = principal; 33 | super.setAuthenticated(true); // must use super, as we override 34 | } 35 | 36 | public Object getCredentials() { 37 | return null; 38 | } 39 | 40 | public Object getPrincipal() { 41 | return this.principal; 42 | } 43 | 44 | public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { 45 | if (isAuthenticated) { 46 | throw new IllegalArgumentException( 47 | "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); 48 | } 49 | super.setAuthenticated(false); 50 | } 51 | 52 | @Override 53 | public void eraseCredentials() { 54 | super.eraseCredentials(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /security-app/src/main/java/com/whyalwaysmea/app/authentication/MyAuthenctiationSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.app.authentication; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.whyalwaysmea.core.properties.LoginResponseType; 5 | import com.whyalwaysmea.core.properties.SecurityProperties; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; 11 | import org.springframework.stereotype.Component; 12 | 13 | import javax.servlet.ServletException; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | 18 | /** 19 | * @Author: HanLong 20 | * @Date: Create in 2018/3/18 16:50 21 | * @Description: 登录成功回调处理 22 | */ 23 | @Component("myAuthenctiationSuccessHandler") 24 | public class MyAuthenctiationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { 25 | 26 | private Logger logger = LoggerFactory.getLogger(getClass()); 27 | 28 | @Autowired 29 | private ObjectMapper objectMapper; 30 | 31 | @Autowired 32 | private SecurityProperties securityProperties; 33 | 34 | @Override 35 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, 36 | Authentication authentication) throws IOException, ServletException { 37 | 38 | logger.info("登录成功"); 39 | 40 | if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginType())) { 41 | response.setContentType("application/json;charset=UTF-8"); 42 | response.getWriter().write(objectMapper.writeValueAsString(authentication)); 43 | } else { 44 | super.onAuthenticationSuccess(request, response, authentication); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/web/interceptor/TimeInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.web.interceptor; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.web.method.HandlerMethod; 5 | import org.springframework.web.servlet.HandlerInterceptor; 6 | import org.springframework.web.servlet.ModelAndView; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.util.Date; 11 | 12 | /** 13 | * @Author: HanLong 14 | * @Date: Create in 2018/3/16 21:24 15 | * @Description: 16 | */ 17 | @Component 18 | public class TimeInterceptor implements HandlerInterceptor { 19 | 20 | @Override 21 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 22 | throws Exception { 23 | System.out.println("preHandle"); 24 | 25 | System.out.println(((HandlerMethod)handler).getBean().getClass().getName()); 26 | System.out.println(((HandlerMethod)handler).getMethod().getName()); 27 | 28 | request.setAttribute("startTime", new Date().getTime()); 29 | return true; 30 | } 31 | 32 | @Override 33 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 34 | ModelAndView modelAndView) throws Exception { 35 | System.out.println("postHandle"); 36 | Long start = (Long) request.getAttribute("startTime"); 37 | System.out.println("time interceptor 耗时:"+ (new Date().getTime() - start)); 38 | 39 | } 40 | 41 | @Override 42 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 43 | throws Exception { 44 | System.out.println("afterCompletion"); 45 | Long start = (Long) request.getAttribute("startTime"); 46 | System.out.println("time interceptor 耗时:"+ (new Date().getTime() - start)); 47 | System.out.println("ex is "+ex); 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/validate/ValidateCodeController.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.validate; 2 | 3 | import com.whyalwaysmea.core.properties.SecurityConstants; 4 | import com.whyalwaysmea.core.properties.SecurityProperties; 5 | import com.whyalwaysmea.core.validate.sms.SmsCodeSender; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.social.connect.web.HttpSessionSessionStrategy; 8 | import org.springframework.social.connect.web.SessionStrategy; 9 | import org.springframework.web.bind.ServletRequestBindingException; 10 | import org.springframework.web.bind.ServletRequestUtils; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import org.springframework.web.context.request.ServletWebRequest; 15 | 16 | import javax.imageio.ImageIO; 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.awt.*; 20 | import java.awt.image.BufferedImage; 21 | import java.io.IOException; 22 | import java.util.Random; 23 | 24 | /** 25 | * @Author: HanLong 26 | * @Date: Create in 2018/3/19 21:12 27 | * @Description: 验证码接口 28 | */ 29 | @RestController 30 | public class ValidateCodeController { 31 | 32 | 33 | @Autowired 34 | private ValidateCodeProcessorHolder validateCodeProcessorHolder; 35 | 36 | /** 37 | * 创建验证码,根据验证码类型不同,调用不同的 {@link ValidateCodeProcessor}接口实现 38 | * 39 | * @param request 40 | * @param response 41 | * @param type 42 | * @throws Exception 43 | */ 44 | @GetMapping(SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/{type}") 45 | public void createCode(HttpServletRequest request, HttpServletResponse response, @PathVariable String type) 46 | throws Exception { 47 | validateCodeProcessorHolder.findValidateCodeProcessor(type).create(new ServletWebRequest(request, response)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /security-browser/src/main/java/com/whyalwaysmea/browser/authentication/MyAuthenctiationSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.browser.authentication; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.whyalwaysmea.core.properties.LoginResponseType; 5 | import com.whyalwaysmea.core.properties.SecurityProperties; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.security.core.Authentication; 11 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; 12 | import org.springframework.stereotype.Component; 13 | 14 | import javax.servlet.ServletException; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | 19 | /** 20 | * @Author: HanLong 21 | * @Date: Create in 2018/3/18 16:50 22 | * @Description: 登录成功回调处理 23 | */ 24 | @Component("myAuthenctiationSuccessHandler") 25 | public class MyAuthenctiationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { 26 | 27 | private Logger logger = LoggerFactory.getLogger(getClass()); 28 | 29 | @Autowired 30 | private ObjectMapper objectMapper; 31 | 32 | @Autowired 33 | private SecurityProperties securityProperties; 34 | 35 | @Override 36 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, 37 | Authentication authentication) throws IOException, ServletException { 38 | 39 | logger.info("登录成功"); 40 | 41 | if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginType())) { 42 | response.setContentType("application/json;charset=UTF-8"); 43 | response.getWriter().write(objectMapper.writeValueAsString(authentication)); 44 | } else { 45 | super.onAuthenticationSuccess(request, response, authentication); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /security-app/src/main/java/com/whyalwaysmea/app/authentication/MyAuthenctiationFailureHandler.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.app.authentication; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.whyalwaysmea.core.dto.BaseResponse; 5 | import com.whyalwaysmea.core.properties.LoginResponseType; 6 | import com.whyalwaysmea.core.properties.SecurityProperties; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.security.core.AuthenticationException; 12 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; 13 | import org.springframework.stereotype.Component; 14 | 15 | import javax.servlet.ServletException; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.IOException; 19 | 20 | /** 21 | * @Author: HanLong 22 | * @Date: Create in 2018/3/18 16:53 23 | * @Description: 登录失败回调处理 24 | */ 25 | @Component("myAuthenctiationFailureHandler") 26 | public class MyAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler { 27 | 28 | private Logger logger = LoggerFactory.getLogger(getClass()); 29 | 30 | @Autowired 31 | private ObjectMapper objectMapper; 32 | 33 | @Autowired 34 | private SecurityProperties securityProperties; 35 | 36 | @Override 37 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, 38 | AuthenticationException exception) throws IOException, ServletException { 39 | 40 | logger.info("登录失败"); 41 | 42 | if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginType())) { 43 | response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); 44 | response.setContentType("application/json;charset=UTF-8"); 45 | response.getWriter().write(objectMapper.writeValueAsString(new BaseResponse(exception.getMessage()))); 46 | }else{ 47 | super.onAuthenticationFailure(request, response, exception); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /security-browser/src/main/java/com/whyalwaysmea/browser/authentication/MyAuthenctiationFailureHandler.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.browser.authentication; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.whyalwaysmea.core.dto.BaseResponse; 5 | import com.whyalwaysmea.core.properties.LoginResponseType; 6 | import com.whyalwaysmea.core.properties.SecurityProperties; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.security.core.AuthenticationException; 12 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; 13 | import org.springframework.stereotype.Component; 14 | 15 | import javax.servlet.ServletException; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.IOException; 19 | 20 | /** 21 | * @Author: HanLong 22 | * @Date: Create in 2018/3/18 16:53 23 | * @Description: 登录失败回调处理 24 | */ 25 | @Component("myAuthenctiationFailureHandler") 26 | public class MyAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler { 27 | 28 | private Logger logger = LoggerFactory.getLogger(getClass()); 29 | 30 | @Autowired 31 | private ObjectMapper objectMapper; 32 | 33 | @Autowired 34 | private SecurityProperties securityProperties; 35 | 36 | @Override 37 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, 38 | AuthenticationException exception) throws IOException, ServletException { 39 | 40 | logger.info("登录失败"); 41 | 42 | // 判断是用哪一种方式进行登录的 43 | // 如果是JSON,则返回JSON字符串;否则进行页面的跳转 44 | if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginType())) { 45 | response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); 46 | response.setContentType("application/json;charset=UTF-8"); 47 | response.getWriter().write(objectMapper.writeValueAsString(new BaseResponse(exception.getMessage()))); 48 | }else{ 49 | super.onAuthenticationFailure(request, response, exception); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/authentication/sms/SmsCodeAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.authentication.sms; 2 | 3 | import org.springframework.security.authentication.AuthenticationProvider; 4 | import org.springframework.security.authentication.InternalAuthenticationServiceException; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.AuthenticationException; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | 10 | /** 11 | * @Author: HanLong 12 | * @Date: Create in 2018/3/24 15:31 13 | * @Description: 类似于{@link DaoAuthenticationProvider} 14 | */ 15 | public class SmsCodeAuthenticationProvider implements AuthenticationProvider { 16 | 17 | private UserDetailsService userDetailsService; 18 | 19 | /** 20 | * 身份逻辑验证 21 | * @param authentication 22 | * @return 23 | * @throws AuthenticationException 24 | */ 25 | @Override 26 | public Authentication authenticate(Authentication authentication) throws AuthenticationException { 27 | 28 | SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication; 29 | 30 | /** 31 | * 调用 {@link UserDetailsService} 32 | */ 33 | UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); 34 | 35 | if (user == null) { 36 | throw new InternalAuthenticationServiceException("无法获取用户信息"); 37 | } 38 | 39 | SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities()); 40 | 41 | authenticationResult.setDetails(authenticationToken.getDetails()); 42 | 43 | return authenticationResult; 44 | } 45 | 46 | @Override 47 | public boolean supports(Class authentication) { 48 | return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication); 49 | } 50 | 51 | public UserDetailsService getUserDetailsService() { 52 | return userDetailsService; 53 | } 54 | 55 | public void setUserDetailsService(UserDetailsService userDetailsService) { 56 | this.userDetailsService = userDetailsService; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.whyalwaysmea.security 8 | security 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | 13 | 1.0-SNAPSHOT 14 | 15 | 16 | 17 | 18 | 19 | io.spring.platform 20 | platform-bom 21 | Brussels-SR4 22 | pom 23 | import 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-dependencies 28 | Dalston.SR2 29 | pom 30 | import 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-compiler-plugin 40 | 2.3.2 41 | 42 | 1.8 43 | 1.8 44 | UTF-8 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | 52 | demo 53 | 54 | 55 | 56 | security-core 57 | security-browser 58 | security-app 59 | security-demo 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /security-core/src/main/java/com/whyalwaysmea/core/authentication/sms/SmsCodeAuthenticationSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.core.authentication.sms; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.authentication.AuthenticationManager; 5 | import org.springframework.security.config.annotation.SecurityConfigurerAdapter; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.web.DefaultSecurityFilterChain; 9 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 10 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 11 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 12 | import org.springframework.stereotype.Component; 13 | 14 | /** 15 | * @Author: HanLong 16 | * @Date: Create in 2018/3/24 15:31 17 | * @Description: sms短信登录配置 18 | */ 19 | @Component 20 | public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter { 21 | 22 | @Autowired 23 | private AuthenticationSuccessHandler myAuthenticationSuccessHandler; 24 | 25 | @Autowired 26 | private AuthenticationFailureHandler myAuthenticationFailureHandler; 27 | 28 | @Autowired 29 | private UserDetailsService userDetailsService; 30 | 31 | @Override 32 | public void configure(HttpSecurity http) throws Exception { 33 | 34 | SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter(); 35 | smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); 36 | smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler); 37 | smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler); 38 | 39 | // 获取验证码提供者 40 | SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider(); 41 | smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService); 42 | 43 | http.authenticationProvider(smsCodeAuthenticationProvider) 44 | .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /security-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | security 7 | com.whyalwaysmea.security 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | security-demo 13 | 14 | 15 | 16 | com.whyalwaysmea.security 17 | security-browser 18 | ${my.security.version} 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-test 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-aop 27 | 28 | 29 | commons-io 30 | commons-io 31 | 32 | 33 | io.springfox 34 | springfox-swagger2 35 | 2.7.0 36 | 37 | 38 | io.springfox 39 | springfox-swagger-ui 40 | 2.7.0 41 | 42 | 43 | com.github.tomakehurst 44 | wiremock 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 1.3.3.RELEASE 54 | 55 | 56 | 57 | repackage 58 | 59 | 60 | 61 | 62 | 63 | demo 64 | 65 | -------------------------------------------------------------------------------- /security-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | security 7 | com.whyalwaysmea.security 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | security-core 13 | 14 | 15 | 16 | org.springframework.cloud 17 | spring-cloud-starter-oauth2 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-data-redis 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-jdbc 26 | 27 | 28 | mysql 29 | mysql-connector-java 30 | 31 | 32 | org.springframework.social 33 | spring-social-config 34 | 35 | 36 | org.springframework.social 37 | spring-social-core 38 | 39 | 40 | org.springframework.social 41 | spring-social-security 42 | 43 | 44 | org.springframework.social 45 | spring-social-web 46 | 47 | 48 | commons-lang 49 | commons-lang 50 | 51 | 52 | commons-collections 53 | commons-collections 54 | 55 | 56 | commons-beanutils 57 | commons-beanutils 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-configuration-processor 62 | 63 | 64 | -------------------------------------------------------------------------------- /security-demo/src/test/java/com/whyalwaysmea/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea; 2 | 3 | import org.apache.commons.lang.builder.ReflectionToStringBuilder; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 13 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers; 14 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 15 | import org.springframework.web.context.WebApplicationContext; 16 | 17 | import java.awt.print.Pageable; 18 | 19 | /** 20 | * @Author: HanLong 21 | * @Date: Create in 2018/3/8 21:16 22 | * @Description: 23 | */ 24 | @RunWith(SpringRunner.class) 25 | @SpringBootTest 26 | public class UserControllerTest { 27 | 28 | @Autowired 29 | private WebApplicationContext context; 30 | 31 | private MockMvc mockMvc; 32 | 33 | @Before 34 | public void setup() { 35 | mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); 36 | } 37 | 38 | @Test 39 | public void whenQuerySuccess() throws Exception { 40 | mockMvc.perform(MockMvcRequestBuilders.get("/user") 41 | .param("username", "haha") 42 | .param("age", "19") 43 | .contentType(MediaType.APPLICATION_JSON_UTF8)) 44 | .andExpect(MockMvcResultMatchers.status().isOk()) 45 | .andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(3)); 46 | } 47 | 48 | @Test 49 | public void whenGetInfoSuccess() throws Exception { 50 | String user = mockMvc.perform(MockMvcRequestBuilders.get("/user/1") 51 | .contentType(MediaType.APPLICATION_JSON_UTF8)) 52 | .andExpect(MockMvcResultMatchers.status().isOk()) 53 | .andExpect(MockMvcResultMatchers.jsonPath("$.userName").value("tom")) 54 | .andReturn().getResponse().getContentAsString(); 55 | System.out.println(user); 56 | } 57 | 58 | @Test 59 | public void whenUpdateSuccess() throws Exception { 60 | String content = "{\"id\":\"1\", \"username\":\"haha\"}"; 61 | String user = mockMvc.perform(MockMvcRequestBuilders.put("/user/199") 62 | .contentType(MediaType.APPLICATION_JSON_UTF8) 63 | .content(content)) 64 | .andExpect(MockMvcResultMatchers.status().isOk()) 65 | .andExpect(MockMvcResultMatchers.jsonPath("$.userName").value("update name")) 66 | .andReturn().getResponse().getContentAsString(); 67 | System.out.println(user); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /security-browser/src/main/java/com/whyalwaysmea/browser/session/AbstractSessionStrategy.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.browser.session; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import com.whyalwaysmea.core.dto.BaseResponse; 7 | import org.apache.commons.lang.StringUtils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.security.web.DefaultRedirectStrategy; 12 | import org.springframework.security.web.RedirectStrategy; 13 | import org.springframework.security.web.util.UrlUtils; 14 | import org.springframework.util.Assert; 15 | 16 | import com.fasterxml.jackson.databind.ObjectMapper; 17 | 18 | import java.io.IOException; 19 | 20 | 21 | /** 22 | * @Author: HanLong 23 | * @Date: Create in 2018/3/27 21:42 24 | * @Description: 25 | */ 26 | public class AbstractSessionStrategy { 27 | 28 | private final Logger logger = LoggerFactory.getLogger(getClass()); 29 | /** 30 | * 跳转的url 31 | */ 32 | private String destinationUrl; 33 | /** 34 | * 重定向策略 35 | */ 36 | private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 37 | /** 38 | * 跳转前是否创建新的session 39 | */ 40 | private boolean createNewSession = true; 41 | 42 | private ObjectMapper objectMapper = new ObjectMapper(); 43 | 44 | public AbstractSessionStrategy(String invalidSessionUrl) { 45 | Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'"); 46 | this.destinationUrl = invalidSessionUrl; 47 | } 48 | 49 | protected void onSessionInvalid(HttpServletRequest request, HttpServletResponse response) throws IOException { 50 | 51 | if (createNewSession) { 52 | request.getSession(); 53 | } 54 | 55 | String sourceUrl = request.getRequestURI(); 56 | String targetUrl; 57 | 58 | if (StringUtils.endsWithIgnoreCase(sourceUrl, ".html")) { 59 | targetUrl = destinationUrl+".html"; 60 | logger.info("session失效,跳转到"+targetUrl); 61 | redirectStrategy.sendRedirect(request, response, targetUrl); 62 | }else{ 63 | String message = "session已失效"; 64 | if(isConcurrency()){ 65 | message = message + ",有可能是并发登录导致的"; 66 | } 67 | response.setStatus(HttpStatus.UNAUTHORIZED.value()); 68 | response.setContentType("application/json;charset=UTF-8"); 69 | response.getWriter().write(objectMapper.writeValueAsString(new BaseResponse(message))); 70 | } 71 | 72 | } 73 | 74 | /** 75 | * session失效是否是并发导致的 76 | * @return 77 | */ 78 | protected boolean isConcurrency() { 79 | return false; 80 | } 81 | 82 | public void setCreateNewSession(boolean createNewSession) { 83 | this.createNewSession = createNewSession; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /security-browser/src/main/java/com/whyalwaysmea/browser/BrowserSecurityController.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.browser; 2 | 3 | import com.whyalwaysmea.core.dto.BaseResponse; 4 | import com.whyalwaysmea.core.properties.SecurityConstants; 5 | import com.whyalwaysmea.core.properties.SecurityProperties; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.security.web.DefaultRedirectStrategy; 11 | import org.springframework.security.web.RedirectStrategy; 12 | import org.springframework.security.web.savedrequest.HttpSessionRequestCache; 13 | import org.springframework.security.web.savedrequest.RequestCache; 14 | import org.springframework.security.web.savedrequest.SavedRequest; 15 | import org.springframework.util.StringUtils; 16 | import org.springframework.web.bind.annotation.GetMapping; 17 | import org.springframework.web.bind.annotation.RequestMapping; 18 | import org.springframework.web.bind.annotation.ResponseStatus; 19 | import org.springframework.web.bind.annotation.RestController; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | import java.io.IOException; 24 | 25 | /** 26 | * @Author: HanLong 27 | * @Date: Create in 2018/3/18 11:19 28 | * @Description: 29 | */ 30 | @RestController 31 | public class BrowserSecurityController { 32 | 33 | private Logger logger = LoggerFactory.getLogger(getClass()); 34 | 35 | // 原请求信息的缓存及恢复 36 | private RequestCache requestCache = new HttpSessionRequestCache(); 37 | 38 | // 用于重定向 39 | private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 40 | 41 | @Autowired 42 | private SecurityProperties securityProperties; 43 | 44 | /** 45 | * 当需要身份认证的时候,跳转过来 46 | * @param request 47 | * @param response 48 | * @return 49 | */ 50 | @RequestMapping(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) 51 | @ResponseStatus(code = HttpStatus.UNAUTHORIZED) 52 | public BaseResponse requireAuthenication(HttpServletRequest request, HttpServletResponse response) throws IOException { 53 | SavedRequest savedRequest = requestCache.getRequest(request, response); 54 | 55 | if (savedRequest != null) { 56 | String targetUrl = savedRequest.getRedirectUrl(); 57 | logger.info("引发跳转的请求是:" + targetUrl); 58 | if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) { 59 | redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage()); 60 | } 61 | } 62 | 63 | return new BaseResponse("访问的服务需要身份认证,请引导用户到登录页"); 64 | } 65 | 66 | 67 | @GetMapping("/session/invalid") 68 | @ResponseStatus(code = HttpStatus.UNAUTHORIZED) 69 | public BaseResponse sessionInvalid() { 70 | System.out.println("哈哈哈"); 71 | return new BaseResponse("Session 超时"); 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /security-demo/src/main/java/com/whyalwaysmea/web/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.whyalwaysmea.web.controller; 2 | 3 | import com.whyalwaysmea.dto.User; 4 | import com.whyalwaysmea.dto.UserQueryCondition; 5 | import org.apache.commons.lang.builder.ReflectionToStringBuilder; 6 | import org.apache.commons.lang.builder.ToStringStyle; 7 | import org.springframework.data.domain.Pageable; 8 | import org.springframework.data.web.PageableDefault; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | import org.springframework.security.core.userdetails.UserDetails; 13 | import org.springframework.validation.BindingResult; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.validation.Valid; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * @Author: HanLong 22 | * @Date: Create in 2018/3/8 21:38 23 | * @Description: 24 | */ 25 | @RestController 26 | @RequestMapping("/user") 27 | public class UserController { 28 | 29 | /** 30 | * 获取当前的用户 31 | * @return 完整的Authentication 32 | */ 33 | @GetMapping("/me1") 34 | public Object currentUser() { 35 | return SecurityContextHolder.getContext().getAuthentication(); 36 | } 37 | 38 | @GetMapping("/me2") 39 | public Object currentUser(Authentication authentication) { 40 | return authentication; 41 | } 42 | 43 | /** 44 | * @param userDetails 45 | * @return 只包含了userDetails 46 | */ 47 | @GetMapping("/me3") 48 | public Object cuurentUser(@AuthenticationPrincipal UserDetails userDetails) { 49 | return userDetails; 50 | } 51 | 52 | @PutMapping("/{id:\\d+}") 53 | public User update(@Valid @RequestBody User user, @PathVariable("id") String id, BindingResult result) { 54 | System.out.println(id); 55 | System.out.println(user); 56 | user.setUserName("update name"); 57 | return user; 58 | } 59 | 60 | @RequestMapping( method = RequestMethod.GET) 61 | public List getUsers(UserQueryCondition userQueryCondition, @PageableDefault(page = 1, size = 20, sort = "username,asc") Pageable pageable) { 62 | // 反射工具,打印对象的属性,值 63 | System.out.println(ReflectionToStringBuilder.toString(pageable, ToStringStyle.MULTI_LINE_STYLE)); 64 | System.out.println(ReflectionToStringBuilder.toString(userQueryCondition, ToStringStyle.MULTI_LINE_STYLE)); 65 | 66 | List 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 | --------------------------------------------------------------------------------