jarvis-security
是一个基于Spring Security 5
的无状态的后端服务的安全框架,引入相应模块可支持QQ
、支付宝
、开源中国
等第三方登录。基于jarvis-security-social
模块,可快速开发自定义的第三方OAuth2.0
协议标准或非标准实现的登录模块。
- 之前很精致的社交登录框架Spring Social停止维护,见Spring Social停止维护声明;
Spring Security 5
作为替代方案,实现了标准的OAuth2.0
协议。但是,国内主流的第三方登录服务提供商如QQ
、支付宝
等出于安全加密或其他未知的原因都不是那么标准(以下简称不标准),所以没法直接用;- 公司正好有个刚上码的项目需要用,不妨写一个。
jarvis-security // 父模块,统一维护依赖版本、公共配置属性、maven 插件配置等,供其他模块引用和继承
├── jarvis-security-core // 核心包,底层的安全配置
├── jarvis-security-social // OAuth2.0第三方登录核心模块,基于该模块可开发自己的第三方登录
├── jarvis-security-social-alipay // 支付宝登录,基于jarvis-security-social模块
├── jarvis-security-social-oschina // 开源中国登录,基于jarvis-security-social模块
└── jarvis-security-social-qq // QQ登录,基于jarvis-security-social模块
-
继承
WebSecurityConfigurerAdapter
接口配置HttpSecurity
的方式将失效!!! -
实现 HttpSecurityConfigurer 接口来配置
HttpSecurity
。
- 配置文件方式
spring:
security:
authorize-requests:
permit-all:
- http-method: GET
path: /actuator/health,/actuator/hystrix.stream
- 实现
AuthorizeRequestsPermitAllProvider
接口
@Component
public class AuthorizeRequestsPermitAll implements AuthorizeRequestsPermitAllProvider {
@Override
public List<Request> getRequests() {
List<Request> requests = new ArrayList<>();
// 指定请求方式
requests.add(new Request("GET", "/actuator/health"));
// 所有请求方式
requests.add(new Request("/actuator/hystrix.stream"));
return requests;
}
}
- 引入依赖
<dependency>
<groupId>io.github.benfromchina</groupId>
<artifactId>jarvis-security-social-qq</artifactId>
<version>1.0.1</version>
</dependency>
- 配置参数
spring:
security:
oauth2:
client:
registration:
qq:
client-id: xxxxxxxxx
client-secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
redirect-uri: http://localhost/login/oauth2/code/qq
- 引入依赖
<dependency>
<groupId>io.github.benfromchina</groupId>
<artifactId>jarvis-security-social-alipay</artifactId>
<version>1.0.1</version>
</dependency>
- 配置参数
spring:
security:
oauth2:
client:
registration:
alipay:
client-id: xxxxxxxxxxxxxxxx
redirect-uri: http://localhost/login/oauth2/code/alipay
# 私钥文件路径,支持 classpath 或磁盘绝对路径,可选配置(实现PrivateKeySupplier接口)
private-key-path: classpath:alipay/应用私钥2048.txt
# 支付宝公钥文件路径,支持 classpath 或磁盘绝对路径,可选配置(实现AlipayPublicKeySupplier接口)
alipay-public-key-path: classpath:alipay/支付宝公钥2048.txt
- 自定义获取私钥接口
实现 PrivateKeySupplier 接口
- 自定义获取支付宝公钥接口
实现 AlipayPublicKeySupplier 接口
- 引入依赖
<dependency>
<groupId>io.github.benfromchina</groupId>
<artifactId>jarvis-security-social-oschina</artifactId>
<version>1.0.1</version>
</dependency>
- 配置参数
spring:
security:
oauth2:
client:
registration:
oschina:
client-id: xxxxxxxxxxxxxxxxxxxx
client-secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
redirect-uri: http://localhost/login/oauth2/code/oschina
注:以下接口描述中的数字与图中对应。
实现 ClientRegistrationBuilderProvider
实现 OAuth2UserConverterProvider
实现 OAuth2AuthorizationRequestEnhancerProvider 接口
实现 OAuth2AuthorizationCodeParameterNameProvider 接口
实现 OAuth2AuthorizationCodeGrantRequestEntityConverterProvider 接口
实现 OAuth2AccessTokenResponseConverterProvider 接口
@Component
public class AkanAccessTokenResponseConverterProvider implements OAuth2AccessTokenResponseConverterProvider {
@Override
public boolean supports(ClientRegistration clientRegistration) {
return Constants.REGISTRATION_ID.equalsIgnoreCase(clientRegistration.getRegistrationId());
}
@Override
public OAuth2AccessTokenResponse convert(ClientRegistration clientRegistration, Map<String, String> tokenResponseParameters) {
try {
tokenResponseParameters = AkanUtils.getData(tokenResponseParameters, clientRegistration.getClientSecret());
} catch (Exception e) {
throw new HttpMessageConversionException(e.getMessage(), e);
}
return convert(tokenResponseParameters);
}
}
实现 OAuth2AccessTokenResponseClientProvider 接口
实现 OAuth2UserRequestEntityConverterProvider 接口
实现 OAuth2UserInfoResponseHttpMessageConverterProvider 接口
实现 OAuth2UserInfoResponseClientProvider 接口
实现 UserConnectionRepository 接口
@Service
public class UserConnectionServiceImpl implements UserConnectionRepository {
@Autowired
private UserService userService;
@Override
public UserConnectionForm saveForm(UserConnectionForm form) {
OAuth2UserDetails formUser = form.getUser();
UserConnection formUserConnection = form.getUserConnection();
UserDetailsImpl user = new UserDetailsImpl();
BeanUtils.copyProperties(formUser, user);
UserConnectionImpl userConnection = new UserConnectionImpl();
BeanUtils.copyProperties(formUserConnection, userConnection);
if (userConnection.getUserId() == null) {
userConnection.setUserId(0l);
}
com.eastsoft.esstock.core.form.manager.UserConnectionForm savedForm = userService.saveUserConnection(new com.eastsoft.esstock.core.form.manager.UserConnectionForm(user, userConnection));
user = new UserDetailsImpl();
BeanUtils.copyProperties(savedForm.getUser(), user);
userConnection = new UserConnectionImpl();
BeanUtils.copyProperties(savedForm.getUserConnection(), userConnection);
return new UserConnectionForm(user, userConnection);
}
}