Compare commits
52
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71ead5c95a | ||
|
|
f66937e5e3 | ||
|
|
9f2673b424 | ||
|
|
0d0840edd7 | ||
|
|
f21a6a156f | ||
|
|
176825f1d3 | ||
|
|
9df28d563f | ||
|
|
87c4c2a2f5 | ||
|
|
22b069a0e9 | ||
|
|
5d1071d704 | ||
|
|
bf6ce3c0ab | ||
|
|
2e46cc6ab7 | ||
|
|
95265e6509 | ||
|
|
9a28f69370 | ||
|
|
52b39dde31 | ||
|
|
e067c8ea2c | ||
|
|
2d359bf8db | ||
|
|
82a7e8a12a | ||
|
|
82be6bbf69 | ||
|
|
b4cc49db4f | ||
|
|
82eb4884e5 | ||
|
|
e917427314 | ||
|
|
1caa12ed77 | ||
|
|
71a9415afb | ||
|
|
9bfde42a6b | ||
|
|
310e629928 | ||
|
|
934f90f15d | ||
|
|
d0a49efd4a | ||
|
|
6b44d9551e | ||
|
|
86c3114cb8 | ||
|
|
462a4de3a6 | ||
|
|
c8cff87398 | ||
|
|
63efcfc54a | ||
|
|
4a946ead68 | ||
|
|
c98f81f045 | ||
|
|
4353188304 | ||
|
|
14a9e80128 | ||
|
|
7d6ca5b708 | ||
|
|
9ea6b1cd5e | ||
|
|
cc7cb41812 | ||
|
|
4ab580e14f | ||
|
|
6bb7847da8 | ||
|
|
8611a50764 | ||
|
|
a1f9db825d | ||
|
|
701a6b2274 | ||
|
|
7bc79359a5 | ||
|
|
63f0a371fd | ||
|
|
51cc5466be | ||
|
|
c5c846abed | ||
|
|
583bd04e91 | ||
|
|
160c0d6a2b | ||
|
|
639f263e83 |
+11
-10
@@ -22,9 +22,14 @@
|
||||
<artifactId>boot-starter-ssm</artifactId>
|
||||
<version>${micro.fast.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.micro.fast</groupId>
|
||||
<artifactId>boot-starter-security-app</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -41,21 +46,17 @@
|
||||
</plugin>
|
||||
<!--生成mybatis dao 层 sql mapper的插件-->
|
||||
<plugin>
|
||||
<groupId>org.mybatis.generator</groupId>
|
||||
<artifactId>mybatis-generator-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<verbose>true</verbose>
|
||||
<overwrite>true</overwrite>
|
||||
</configuration>
|
||||
<groupId>org.mybatis.generator</groupId>
|
||||
<artifactId>mybatis-generator-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<verbose>true</verbose>
|
||||
<overwrite>false</overwrite>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<!--<plugin>-->
|
||||
<!--<groupId>org.apache.maven.plugins</groupId>-->
|
||||
<!--<artifactId>maven-javadoc-plugin</artifactId>-->
|
||||
<!--</plugin>-->
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
|
||||
+2
-3
@@ -7,9 +7,8 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
@MapperScan(basePackages = {"com.micro.fast.pa.dao"})
|
||||
public class PerformanceAppraisalApplication {
|
||||
|
||||
public class AllInOneApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PerformanceAppraisalApplication.class, args);
|
||||
SpringApplication.run(AllInOneApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,8 @@ server:
|
||||
jdbc:
|
||||
master:
|
||||
username: root
|
||||
password: hfAhyoTu3UKAfmy27/RNKQ==
|
||||
url: jdbc:mysql://127.0.0.1:3317/micro?useUncoide=true&chracterEncoding=utf-8&useSSL=true
|
||||
password: studyj2e
|
||||
url: jdbc:mysql://127.0.0.1:3317/micro?useUncoide=true&chracterEncoding=utf-8&useSSL=false
|
||||
driverClass: com.mysql.jdbc.Driver
|
||||
mybatis:
|
||||
typeAliasesPackage: com.lishouyu.pojo
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class PerformanceAppraisalApplicationTests {
|
||||
public class AllInOneApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
@@ -104,7 +104,7 @@ var activeTableTab = "activeTableTab";
|
||||
<li>java.lang.Enum<<a href="../../../../../com/micro/fast/common/response/Const.ServerResponseCode.html" title="com.micro.fast.common.response中的枚举">Const.ServerResponseCode</a>></li>
|
||||
<li>
|
||||
<ul class="inheritance">
|
||||
<li>com.micro.fast.common.response.Const.ServerResponseCode</li>
|
||||
<li>com.micro.fast.common.response.BaseConst.ServerResponseCode</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
<li>java.lang.Object</li>
|
||||
<li>
|
||||
<ul class="inheritance">
|
||||
<li>com.micro.fast.common.response.Const</li>
|
||||
<li>com.micro.fast.common.response.BaseConst</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_144) on Mon Oct 02 18:41:08 CST 2017 -->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>类 com.micro.fast.common.response.Const.ServerResponseCode的使用</title>
|
||||
<title>类 com.micro.fast.common.response.BaseConst.ServerResponseCode的使用</title>
|
||||
<meta name="date" content="2017-10-02">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
@@ -13,7 +13,7 @@
|
||||
<script type="text/javascript"><!--
|
||||
try {
|
||||
if (location.href.indexOf('is-external=true') == -1) {
|
||||
parent.document.title="\u7C7B com.micro.fast.common.response.Const.ServerResponseCode\u7684\u4F7F\u7528";
|
||||
parent.document.title="\u7C7B com.micro.fast.common.response.BaseConst.ServerResponseCode\u7684\u4F7F\u7528";
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
@@ -71,7 +71,7 @@
|
||||
</a></div>
|
||||
<!-- ========= END OF TOP NAVBAR ========= -->
|
||||
<div class="header">
|
||||
<h2 title="类的使用 com.micro.fast.common.response.Const.ServerResponseCode" class="title">类的使用<br>com.micro.fast.common.response.Const.ServerResponseCode</h2>
|
||||
<h2 title="类的使用 com.micro.fast.common.response.BaseConst.ServerResponseCode" class="title">类的使用<br>com.micro.fast.common.response.BaseConst.ServerResponseCode</h2>
|
||||
</div>
|
||||
<div class="classUseContainer">
|
||||
<ul class="blockList">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<script type="text/javascript"><!--
|
||||
try {
|
||||
if (location.href.indexOf('is-external=true') == -1) {
|
||||
parent.document.title="\u7C7B com.micro.fast.common.response.Const\u7684\u4F7F\u7528";
|
||||
parent.document.title="\u7C7B com.micro.fast.common.response.BaseConst\u7684\u4F7F\u7528";
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
@@ -71,7 +71,7 @@
|
||||
</a></div>
|
||||
<!-- ========= END OF TOP NAVBAR ========= -->
|
||||
<div class="header">
|
||||
<h2 title="类的使用 com.micro.fast.common.response.Const" class="title">类的使用<br>com.micro.fast.common.response.Const</h2>
|
||||
<h2 title="类的使用 com.micro.fast.common.response.BaseConst" class="title">类的使用<br>com.micro.fast.common.response.BaseConst</h2>
|
||||
</div>
|
||||
<div class="classUseContainer">没有com.micro.fast.common.response.Const的用法</div>
|
||||
<!-- ======= START OF BOTTOM NAVBAR ====== -->
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-- This SQL contains a "create table" that can be used to create a table that JdbcUsersConnectionRepository can persist
|
||||
-- connection in. It is, however, not to be assumed to be production-ready, all-purpose SQL. It is merely representative
|
||||
-- of the kind of table that JdbcUsersConnectionRepository works with. The table and column names, as well as the general
|
||||
-- column types, are what is important. Specific column types and sizes that work may vary across database vendors and
|
||||
-- the required sizes may vary across API providers.
|
||||
USE micro;
|
||||
create table upms_UserConnection (userId varchar(255) not null,
|
||||
providerId varchar(255) not null,
|
||||
providerUserId varchar(255),
|
||||
rank int not null,
|
||||
displayName varchar(255),
|
||||
profileUrl varchar(512),
|
||||
imageUrl varchar(512),
|
||||
accessToken varchar(512) not null,
|
||||
secret varchar(512),
|
||||
refreshToken varchar(512),
|
||||
expireTime bigint,
|
||||
primary key (userId, providerId, providerUserId));
|
||||
create unique index UserConnectionRank on upms_UserConnection(userId, providerId, rank);
|
||||
+1
-1
@@ -49,7 +49,7 @@ DROP TABLE IF EXISTS ucenter_user;
|
||||
CREATE TABLE ucenter_user (
|
||||
`id` INT(10) UNSIGNED AUTO_INCREMENT NOT NULL
|
||||
COMMENT '编号',
|
||||
`password` VARCHAR(32) NOT NULL
|
||||
`password` VARCHAR(200) NOT NULL
|
||||
COMMENT '密码(MD5(密码+salt))',
|
||||
`salt` VARCHAR(32) DEFAULT NULL
|
||||
COMMENT '盐值',
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ CREATE TABLE upms_user (
|
||||
COMMENT '用户的编号',
|
||||
`username` VARCHAR(20) NOT NULL
|
||||
COMMENT '用户名',
|
||||
`password` VARCHAR(32) NOT NULL
|
||||
`password` VARCHAR(200) NOT NULL
|
||||
COMMENT '密码加密后的md5值(密码+盐值)',
|
||||
`salt` VARCHAR(32) DEFAULT NULL
|
||||
COMMENT '盐值',
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
- oauth2请求的用户必须有一个ROLE_USER的用户权限
|
||||
- 相同的用户,会得到相同的token
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.micro.fast.security.app;
|
||||
|
||||
import com.micro.fast.security.app.social.AppSingUpUtils;
|
||||
import com.micro.fast.security.core.pojo.SocialUserInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.social.connect.Connection;
|
||||
import org.springframework.social.connect.web.ProviderSignInUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
*/
|
||||
@RestController
|
||||
public class AppSecurityController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private AppSingUpUtils singUpUtils;
|
||||
|
||||
@Autowired
|
||||
private ProviderSignInUtils providerSignInUtils;
|
||||
|
||||
/**
|
||||
* 引导用户注册,关联第三方账户,把用户的信息存储到redis之中
|
||||
* 需要被授权
|
||||
* 在注册接口之,需要从redis之中获取数据进行注册
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/social/signUp")
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
public SocialUserInfo getSocialUserInfo(HttpServletRequest request){
|
||||
SocialUserInfo socialUserInfo = new SocialUserInfo();
|
||||
Connection<?> connectionFromSession = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
|
||||
socialUserInfo.setProviderId(connectionFromSession.getKey().getProviderId());
|
||||
socialUserInfo.setProviderUserId(connectionFromSession.getKey().getProviderUserId());
|
||||
socialUserInfo.setHeadingUrl(connectionFromSession.getDisplayName());
|
||||
socialUserInfo.setHeadingUrl(connectionFromSession.getImageUrl());
|
||||
singUpUtils.saveConnectionData(new ServletWebRequest(request),connectionFromSession.createData());
|
||||
return socialUserInfo;
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.micro.fast.security.app;
|
||||
|
||||
import com.micro.fast.security.core.validate.code.ValidateCodeRepository;
|
||||
import com.micro.fast.security.core.validate.code.exception.ValidateCodeException;
|
||||
import com.micro.fast.security.core.validate.code.pojo.ValidateCode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* app模块存储验证码
|
||||
* @author lsy
|
||||
*/
|
||||
@Component
|
||||
public class AppValidateCodeRepository implements ValidateCodeRepository {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object,Object> redisTemplate;
|
||||
|
||||
@Override
|
||||
public void save(ServletWebRequest request, ValidateCode code, String validateCodeType) {
|
||||
redisTemplate.opsForValue().set(buildKey(request,validateCodeType),code,30, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidateCode get(ServletWebRequest request, String validateCodeType) {
|
||||
Object o = redisTemplate.opsForValue().get(buildKey(request, validateCodeType));
|
||||
if (o==null){
|
||||
return null;
|
||||
}
|
||||
return (ValidateCode) o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(ServletWebRequest request, String validateCodeType) {
|
||||
redisTemplate.delete(buildKey(request,validateCodeType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建redis的key
|
||||
* @param request
|
||||
* @param validateCodeType
|
||||
* @return
|
||||
*/
|
||||
private String buildKey(ServletWebRequest request,String validateCodeType){
|
||||
String deviceId = request.getHeader("deviceId");
|
||||
if (StringUtils.isBlank(deviceId)){
|
||||
throw new ValidateCodeException("请求头中缺少机器的唯一识别");
|
||||
}
|
||||
return "code:"+validateCodeType.toLowerCase()+":"+deviceId;
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -1,8 +1,15 @@
|
||||
package com.micro.fast.security.app;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@SpringBootApplication
|
||||
/**
|
||||
* 自动配置入口类
|
||||
* @author lsy
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
public class BootStarterSecurityAppApplication {
|
||||
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.micro.fast.security.app;
|
||||
|
||||
import com.micro.fast.security.core.social.MsSpringSocialConfigurer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* spring中所有的bean初始化之前和初始化之后都要经过这两个方法
|
||||
* @author lsy
|
||||
*/
|
||||
@Component
|
||||
public class SpringSocialConfigurePostProcessor implements BeanPostProcessor{
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (StringUtils.equals(beanName,"msSpringSocialConfigurer")){
|
||||
MsSpringSocialConfigurer configurer = (MsSpringSocialConfigurer)bean;
|
||||
//app第三方用户的注册
|
||||
configurer.signupUrl("/social/signUp");
|
||||
return configurer;
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.micro.fast.security.app;
|
||||
|
||||
import com.micro.fast.security.app.jwt.MsJwtEnhancer;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
|
||||
|
||||
/**
|
||||
* 配置令牌的存取策略
|
||||
* @author lsy
|
||||
*/
|
||||
@Configuration
|
||||
public class TokenStoreConfig {
|
||||
|
||||
@Autowired
|
||||
private RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "ms.security.oauth2",name = "storeType",havingValue = "redis",matchIfMissing = false)
|
||||
public TokenStore redisTokenStore(){
|
||||
return new RedisTokenStore(redisConnectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果存在ms.security.oauth2.storeType的配置存在且值是jwt这个配置就生效
|
||||
* 如果不存在这个ms.security.oauth2.storeType属性就默认生效
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "ms.security.oauth2",name = "storeType",havingValue = "jwt",matchIfMissing = true)
|
||||
public static class JwtTokenConfig{
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
|
||||
@Bean
|
||||
public TokenStore jwtTokenStore(){
|
||||
return new JwtTokenStore(jwtAccessTokenConverter());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAccessTokenConverter jwtAccessTokenConverter(){
|
||||
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
|
||||
//根据秘钥生成token
|
||||
jwtAccessTokenConverter.setSigningKey(securityProperties.getOauth2().getJwtSigningKey());
|
||||
return jwtAccessTokenConverter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TokenEnhancer tokenEnhancer(){
|
||||
return new MsJwtEnhancer();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.micro.fast.security.app.config.authorization2;
|
||||
|
||||
import com.micro.fast.security.core.master.OAuth2ClientProperties;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.sun.org.apache.regexp.internal.RE;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
|
||||
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* OAuth2协议标准服务商实现配置类,配置token的生成存储策略
|
||||
* @author lsy
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAuthorizationServer
|
||||
public class AuthorizationProviderConfig extends AuthorizationServerConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Autowired(required = true)
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
/**
|
||||
* 只有在jwt环境下才生效
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
private JwtAccessTokenConverter jwtAccessTokenConverter;
|
||||
|
||||
@Autowired
|
||||
private TokenEnhancer jwtTokenEnhancer;
|
||||
|
||||
@Autowired
|
||||
private TokenStore tokenStore;
|
||||
|
||||
/**
|
||||
* 配置token的存取策略,以及增强策略
|
||||
* @param endpoints
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
|
||||
endpoints
|
||||
.tokenStore(tokenStore)
|
||||
.authenticationManager(authenticationManager)
|
||||
.userDetailsService(userDetailsService);
|
||||
if (jwtAccessTokenConverter!=null && jwtTokenEnhancer!=null){
|
||||
//声明token增强链
|
||||
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
|
||||
//将增强逻辑填入增强链
|
||||
List<TokenEnhancer> enhancers = new ArrayList<>();
|
||||
enhancers.add(jwtTokenEnhancer);
|
||||
enhancers.add(jwtAccessTokenConverter);
|
||||
enhancerChain.setTokenEnhancers(enhancers);
|
||||
//将增强链放入扩展点中
|
||||
endpoints.tokenEnhancer(enhancerChain)
|
||||
.accessTokenConverter(jwtAccessTokenConverter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置哪些应用可以获取token
|
||||
* @param clients
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||
InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
|
||||
if (ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())){
|
||||
for (OAuth2ClientProperties client: securityProperties.getOauth2().getClients()){
|
||||
builder.withClient(client.getClientId()).secret(client.getClientSecret())
|
||||
.accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
|
||||
.authorizedGrantTypes("refresh_token","password")
|
||||
//refreshToken的过期时间,refreshToken是用来在用户无感知的情况下换取token的
|
||||
.refreshTokenValiditySeconds(2592000)
|
||||
.scopes("all","read","write");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package com.micro.fast.security.app.config.authorization2;
|
||||
|
||||
import com.micro.fast.security.app.config.component.MsAuthenticationFailureHandler;
|
||||
import com.micro.fast.security.app.config.component.MsAuthenticationSuccessHandler;
|
||||
import com.micro.fast.security.app.social.openid.OpenIdAuthenticationSecurityConfig;
|
||||
import com.micro.fast.security.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig;
|
||||
import com.micro.fast.security.core.master.SecurityConstants;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.core.validate.code.ValidateCodeRepository;
|
||||
import com.micro.fast.security.core.validate.code.filter.ValidateCodeFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.social.security.SpringSocialConfigurer;
|
||||
|
||||
/**
|
||||
* OAuth2协议标准服务提供实现配置类,资源服务器的配置
|
||||
* @author lsy
|
||||
*/
|
||||
@Configuration
|
||||
@EnableResourceServer
|
||||
public class AuthorizationResourceConfig extends ResourceServerConfigurerAdapter {
|
||||
|
||||
/**
|
||||
* todo 待优化,不唯一,应从启动参数中获取
|
||||
*/
|
||||
public static final String DRUID = "/druid/*";
|
||||
|
||||
@Autowired
|
||||
private MsAuthenticationFailureHandler msAuthenticationFailureHandler;
|
||||
|
||||
@Autowired
|
||||
private MsAuthenticationSuccessHandler msAuthenticationSuccessHandler;
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
/**
|
||||
* 短信验证码登录的配置
|
||||
*/
|
||||
@Autowired
|
||||
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
|
||||
|
||||
@Autowired
|
||||
private SpringSocialConfigurer springSocialConfigurer;
|
||||
|
||||
@Autowired
|
||||
private ValidateCodeRepository validateCodeRepository;
|
||||
|
||||
@Autowired
|
||||
private OpenIdAuthenticationSecurityConfig openIdAuthenticationSecurityConfig;
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
//创建自定义的验证码过滤器,填入身份认证失败处理对象
|
||||
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
|
||||
validateCodeFilter.setAuthenticationFailureHandler(msAuthenticationFailureHandler);
|
||||
validateCodeFilter.setSecurityProperties(securityProperties);
|
||||
validateCodeFilter.setValidateCodeRepository(validateCodeRepository);
|
||||
validateCodeFilter.afterPropertiesSet();
|
||||
|
||||
http
|
||||
//添加自定义的验证码处理器,在用户密码校验过滤器的方式获取令牌之前
|
||||
.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
//使用表单登录
|
||||
.formLogin()
|
||||
//自定义登录页面
|
||||
.loginPage(SecurityConstants.LOGIN_PAGE)
|
||||
//配置spring security登录接口的访问地址,用于在自定义的登录页面提交请求
|
||||
.loginProcessingUrl(SecurityConstants.FORM_LOGIN_PROCESSOR_URI)
|
||||
//定义身份认证成功后处理对象
|
||||
.successHandler(msAuthenticationSuccessHandler)
|
||||
//定义身份认证失败后处理对象
|
||||
.failureHandler(msAuthenticationFailureHandler)
|
||||
.and()
|
||||
.authorizeRequests()//下面是权限配置
|
||||
//当访问这个页面的时候不进行身份认证,对用户自定义的登录页也不进行权限认证
|
||||
.antMatchers(SecurityConstants.MOBILE_LOGIN_PROCESSOR_URI
|
||||
, SecurityConstants.FORM_LOGIN_PROCESSOR_URI
|
||||
, SecurityConstants.GET_VALIDATE_CODE_URI
|
||||
, SecurityConstants.LOGIN_PAGE
|
||||
, DRUID
|
||||
, securityProperties.getBrowser().getHtml()
|
||||
//用户注册或绑定的页面
|
||||
, securityProperties.getBrowser().getSignUpUrl()
|
||||
, "处理用户的注册绑定请求的url"
|
||||
,"/social/signUp"
|
||||
)
|
||||
.permitAll()
|
||||
//任何请求
|
||||
.anyRequest()
|
||||
//都需要身份认证
|
||||
.authenticated()
|
||||
.and()
|
||||
//暂时关闭掉跨站请求攻击防护
|
||||
.csrf().disable()
|
||||
//应用短信验证码登录的逻辑
|
||||
.apply(smsCodeAuthenticationSecurityConfig)
|
||||
.and()
|
||||
//应用springSocial的配置,在接收到特殊的请求的时候进行社交登录.例如 /auth/qq 其中/auth是默认的前缀,qq是providerId
|
||||
.apply(springSocialConfigurer)
|
||||
.and()
|
||||
//openid
|
||||
.apply(openIdAuthenticationSecurityConfig);
|
||||
;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.app.config.authorization2;
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.micro.fast.security.app.config.component;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.micro.fast.security.core.browser.constant.LoginType;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* AuthenticationFailureHandler
|
||||
* 身份认证失败的后续处理类
|
||||
* @author lsy
|
||||
*/
|
||||
@Component("msAuthenticationFailureHandler")
|
||||
public class MsAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(MsAuthenticationFailureHandler.class);
|
||||
/**
|
||||
* object <=> json 处理对象
|
||||
*/
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
/**
|
||||
* 安全框架配置参数对象
|
||||
*/
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(
|
||||
HttpServletRequest httpServletRequest
|
||||
, HttpServletResponse httpServletResponse
|
||||
, AuthenticationException e)
|
||||
throws IOException, ServletException {
|
||||
//根据配置参数判断身份认证失败后的返回类型进行响应
|
||||
if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
|
||||
log.info("failure");
|
||||
httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
httpServletResponse.setContentType("application/json;charset=UTF-8");
|
||||
httpServletResponse.getWriter().write(objectMapper.writeValueAsString(e));
|
||||
}else{
|
||||
super.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package com.micro.fast.security.app.config.component;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 身份认证成功后的处理类,在登录成功后进行授权token的处理
|
||||
*
|
||||
* @author lsy
|
||||
*/
|
||||
@Component("msAuthenticationSuccessHandler")
|
||||
public class MsAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MsAuthenticationSuccessHandler.class);
|
||||
|
||||
/**
|
||||
* 安全框架配置参数对象
|
||||
*/
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
/**
|
||||
* object <=> json 处理对象
|
||||
*/
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private ClientDetailsService clientDetailsService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("defaultAuthorizationServerTokenServices")
|
||||
private AuthorizationServerTokenServices authorizationServerTokenServices;
|
||||
/**
|
||||
* 登录成功后的处理方法
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param authentication 用户认证的详细信息
|
||||
* @throws IOException
|
||||
* @throws ServletException
|
||||
*/
|
||||
@Override
|
||||
public void onAuthenticationSuccess(
|
||||
HttpServletRequest request
|
||||
, HttpServletResponse response
|
||||
, Authentication authentication
|
||||
) throws IOException, ServletException {
|
||||
log.info("登录成功,开始授权令牌");
|
||||
//1.校验clientId和clientSecret是否对应
|
||||
String header = request.getHeader("Authorization");
|
||||
if (header == null && !header.startsWith("Basic ")) {
|
||||
throw new UnapprovedClientAuthenticationException("请求头中client信息不存在");
|
||||
}
|
||||
String[] tokens = this.extractAndDecodeHeader(header, request);
|
||||
assert tokens.length == 2;
|
||||
String clientId = tokens[0];
|
||||
String clientSecret = tokens[1];
|
||||
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
|
||||
if (clientDetails == null){
|
||||
throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在:"+clientId);
|
||||
}else if (!StringUtils.equals(clientDetails.getClientSecret(),clientSecret)){
|
||||
throw new UnapprovedClientAuthenticationException("clientSecret不匹配:"+clientSecret);
|
||||
}
|
||||
//2.自定义流程创建access_Token
|
||||
TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP,clientId,clientDetails.getScope(),"custom");
|
||||
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
|
||||
OAuth2Authentication auth2Authentication = new OAuth2Authentication(oAuth2Request,authentication);
|
||||
OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(auth2Authentication);
|
||||
//3.返回accessToken
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write(objectMapper.writeValueAsString(accessToken));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 对header进行解码
|
||||
* @param header
|
||||
* @param request
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {
|
||||
byte[] base64Token = header.substring(6).getBytes("UTF-8");
|
||||
|
||||
byte[] decoded;
|
||||
try {
|
||||
decoded = Base64.decode(base64Token);
|
||||
} catch (IllegalArgumentException var7) {
|
||||
throw new BadCredentialsException("Failed to decode basic authentication token");
|
||||
}
|
||||
|
||||
String token = new String(decoded, this.getCredentialsCharset(request));
|
||||
int delim = token.indexOf(":");
|
||||
if (delim == -1) {
|
||||
throw new BadCredentialsException("Invalid basic authentication token");
|
||||
} else {
|
||||
return new String[]{token.substring(0, delim), token.substring(delim + 1)};
|
||||
}
|
||||
}
|
||||
|
||||
protected String getCredentialsCharset(HttpServletRequest httpRequest) {
|
||||
return "UTF-8";
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.app.config.component;
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.app.config;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.micro.fast.security.app.jwt;
|
||||
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 对jwt中的信息进行自定义
|
||||
* @author lsy
|
||||
*/
|
||||
public class MsJwtEnhancer implements TokenEnhancer{
|
||||
@Override
|
||||
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
|
||||
//添加自定义的信息,在这里面可以填入角色信息
|
||||
Map<String,Object> info = new HashMap<>();
|
||||
info.put("project","ms");
|
||||
((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);
|
||||
return oAuth2AccessToken;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.micro.fast.security.app.social;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.social.connect.Connection;
|
||||
import org.springframework.social.connect.ConnectionData;
|
||||
import org.springframework.social.connect.ConnectionFactoryLocator;
|
||||
import org.springframework.social.connect.UsersConnectionRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* app第三方登录的工具类
|
||||
* @author lsy
|
||||
*/
|
||||
@Component
|
||||
public class AppSingUpUtils {
|
||||
|
||||
@Autowired
|
||||
private UsersConnectionRepository usersConnectionRepository;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object,Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private ConnectionFactoryLocator connectionFactoryLocator;
|
||||
|
||||
/**
|
||||
* 存储第三方账号关联信息
|
||||
* @param request
|
||||
* @param connectionData
|
||||
*/
|
||||
public void saveConnectionData(WebRequest request, ConnectionData connectionData){
|
||||
redisTemplate.opsForValue().set(getKey(request),connectionData,10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册第三方的用户信息
|
||||
* @param request
|
||||
* @param userId
|
||||
*/
|
||||
public void doPostSignUp(WebRequest request,String userId){
|
||||
String key = getKey(request);
|
||||
if (!redisTemplate.hasKey(key)){
|
||||
throw new RuntimeException("无法找到缓存的第三方用户信息");
|
||||
}
|
||||
ConnectionData connectionData = (ConnectionData)redisTemplate.opsForValue().get(getKey(request));
|
||||
Connection<?> connection = connectionFactoryLocator.getConnectionFactory(connectionData.getProviderId()).createConnection(connectionData);
|
||||
usersConnectionRepository.createConnectionRepository(userId).addConnection(connection);
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建redis的key
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
protected String getKey(WebRequest request){
|
||||
String deviceId = request.getHeader("deviceId");
|
||||
if (StringUtils.isBlank(deviceId)){
|
||||
throw new RuntimeException("机器唯一标识参数不能为空");
|
||||
}
|
||||
return "ms:security:social.connect."+deviceId;
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.micro.fast.security.app.social.impl;
|
||||
|
||||
import com.micro.fast.security.core.social.SocialAuthenticationFilterPostProcessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.social.security.SocialAuthenticationFilter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 社交登录成功后的后处理器实现
|
||||
* @author lsy
|
||||
*/
|
||||
@Component
|
||||
public class AppSocialAuthenticationFilterProcessor implements SocialAuthenticationFilterPostProcessor {
|
||||
|
||||
@Autowired
|
||||
private AuthenticationSuccessHandler successHandler;
|
||||
|
||||
@Override
|
||||
public void process(SocialAuthenticationFilter socialAuthenticationFilter) {
|
||||
//传入授权成功处理器
|
||||
socialAuthenticationFilter.setAuthenticationSuccessHandler(successHandler);
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.micro.fast.security.app.social.openid;
|
||||
|
||||
import com.micro.fast.security.core.master.SecurityConstants;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* openId登录身份认证过滤器
|
||||
* @author lsy
|
||||
*/
|
||||
public class OpenIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
/**
|
||||
* 从请求中获取openId的key
|
||||
*/
|
||||
private String openIdParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_OPNEID;
|
||||
|
||||
/**
|
||||
* 从请求中获取providerId的key
|
||||
*/
|
||||
private String providerIdParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_PROVIDERID;
|
||||
/**
|
||||
* 是不是只处理post请求
|
||||
*/
|
||||
private boolean postOnly = true;
|
||||
|
||||
/**
|
||||
* 对/authentication/openid路径的post请求进行身份登录操作
|
||||
*/
|
||||
public OpenIdAuthenticationFilter() {
|
||||
super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESS_URL_OPENID, "POST"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 主要的身份认证方法
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
||||
if (this.postOnly && !request.getMethod().equals("POST")) {
|
||||
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
|
||||
} else {
|
||||
//获取请求中的mobile
|
||||
String openid = this.obtainMobile(request);
|
||||
if (openid == null) {
|
||||
openid = "";
|
||||
}
|
||||
openid = openid.trim();
|
||||
String providerId = request.getParameter(this.providerIdParameter);
|
||||
if (providerId == null){
|
||||
providerId = "";
|
||||
}
|
||||
providerId = providerId.trim();
|
||||
OpenIdAuthenticationToken authRequest = new OpenIdAuthenticationToken(openid,providerId);
|
||||
this.setDetails(request, authRequest);
|
||||
//调用AuthenticationManager中的方法
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取openId的方法
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
protected String obtainMobile(HttpServletRequest request) {
|
||||
return request.getParameter(this.openIdParameter);
|
||||
}
|
||||
|
||||
protected void setDetails(HttpServletRequest request, OpenIdAuthenticationToken authRequest) {
|
||||
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||
}
|
||||
|
||||
public void setOpenIdParameter(String openIdParameter) {
|
||||
Assert.hasText(openIdParameter, "Username parameter must not be empty or null");
|
||||
this.openIdParameter = openIdParameter;
|
||||
}
|
||||
|
||||
public void setPostOnly(boolean postOnly) {
|
||||
this.postOnly = postOnly;
|
||||
}
|
||||
|
||||
public final String getOpenIdParameter() {
|
||||
return this.openIdParameter;
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.micro.fast.security.app.social.openid;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.social.connect.UsersConnectionRepository;
|
||||
import org.springframework.social.security.SocialUserDetailsService;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 进行openId的身份认证提供类
|
||||
* @author lsy
|
||||
*/
|
||||
public class OpenIdAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private SocialUserDetailsService userDetailsService;
|
||||
|
||||
private UsersConnectionRepository usersConnectionRepository;
|
||||
/**
|
||||
* 返回经过验证的Authentication
|
||||
* @param authentication
|
||||
* @return
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OpenIdAuthenticationToken openIdAuthenticationToken = (OpenIdAuthenticationToken) authentication;
|
||||
Set<String> providerUserIds = new HashSet<>();
|
||||
String principal = (String)openIdAuthenticationToken.getPrincipal();
|
||||
providerUserIds.add(principal);
|
||||
Set<String> userIds = usersConnectionRepository.findUserIdsConnectedTo(openIdAuthenticationToken.getProviderId(), providerUserIds);
|
||||
if (CollectionUtils.isEmpty(userIds) || userIds.size()!=1) {
|
||||
throw new InternalAuthenticationServiceException("无法获取用户的信息");
|
||||
}
|
||||
String userId = userIds.iterator().next();
|
||||
UserDetails socialUserDetails = userDetailsService.loadUserByUserId(userId);
|
||||
if (socialUserDetails == null){
|
||||
throw new InternalAuthenticationServiceException("无法获取用户的信息");
|
||||
}
|
||||
OpenIdAuthenticationToken openIdAuthenticationTokenResult = new OpenIdAuthenticationToken(socialUserDetails, socialUserDetails.getAuthorities());
|
||||
openIdAuthenticationTokenResult.setDetails(openIdAuthenticationToken.getDetails());
|
||||
return openIdAuthenticationTokenResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个类是不是支持传入的token的校验
|
||||
* @param authenticate
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> authenticate) {
|
||||
return OpenIdAuthenticationToken.class.isAssignableFrom(authenticate);
|
||||
}
|
||||
|
||||
public SocialUserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(SocialUserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public UsersConnectionRepository getUsersConnectionRepository() {
|
||||
return usersConnectionRepository;
|
||||
}
|
||||
|
||||
public void setUsersConnectionRepository(UsersConnectionRepository usersConnectionRepository) {
|
||||
this.usersConnectionRepository = usersConnectionRepository;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.micro.fast.security.app.social.openid;
|
||||
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.social.connect.UsersConnectionRepository;
|
||||
import org.springframework.social.security.SocialUserDetailsService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
* openId登录的配置类
|
||||
*/
|
||||
@Component
|
||||
public class OpenIdAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,HttpSecurity>{
|
||||
|
||||
@Autowired
|
||||
@Qualifier("msAuthenticationSuccessHandler")
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("msAuthenticationFailureHandler")
|
||||
private AuthenticationFailureHandler authenticationFailureHandler;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("msUserDetailsServiceImpl")
|
||||
private SocialUserDetailsService userDetailsService ;
|
||||
|
||||
@Autowired
|
||||
private UsersConnectionRepository usersConnectionRepository;
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
|
||||
OpenIdAuthenticationFilter OpenIdAuthenticationFilter = new OpenIdAuthenticationFilter();
|
||||
//将短信验证码登录过滤器放入AuthenticationManager,方便后续调用
|
||||
OpenIdAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
|
||||
OpenIdAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
|
||||
OpenIdAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
|
||||
|
||||
//将根据手机号获取用户信息的对象放入短信验证码的provider中
|
||||
OpenIdAuthenticationProvider openIdAuthenticationProvider = new OpenIdAuthenticationProvider();
|
||||
openIdAuthenticationProvider.setUserDetailsService(userDetailsService);
|
||||
openIdAuthenticationProvider.setUsersConnectionRepository(usersConnectionRepository);
|
||||
//将自定义的provider放入authenticaionManger管理的集合中去
|
||||
http.authenticationProvider(openIdAuthenticationProvider)
|
||||
//将自定义的过滤器添加到用户账号密码校验过滤器后面
|
||||
.addFilterAfter(OpenIdAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.micro.fast.security.app.social.openid;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* APP社交登陆中使用openId进行登录的token
|
||||
* @author lsy
|
||||
*/
|
||||
public class OpenIdAuthenticationToken extends AbstractAuthenticationToken {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Object principal;
|
||||
|
||||
/**
|
||||
* 服务提供商的id
|
||||
*/
|
||||
private String providerId;
|
||||
|
||||
/**
|
||||
* 在没有进行身份认证的时候存储手机号
|
||||
* @param openId openId
|
||||
*/
|
||||
public OpenIdAuthenticationToken(String openId,String providerId) {
|
||||
super((Collection)null);
|
||||
this.principal = openId;
|
||||
this.providerId = providerId;
|
||||
this.setAuthenticated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param principal
|
||||
* @param authorities
|
||||
*/
|
||||
public OpenIdAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
super.setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
|
||||
if (isAuthenticated) {
|
||||
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
|
||||
} else {
|
||||
super.setAuthenticated(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eraseCredentials() {
|
||||
super.eraseCredentials();
|
||||
}
|
||||
|
||||
public String getProviderId() {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
public void setProviderId(String providerId) {
|
||||
this.providerId = providerId;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.app.social;
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.micro.fast.security.app.BootStarterSecurityAppApplication
|
||||
@@ -2,9 +2,19 @@
|
||||
|
||||
##相关配置参数说明
|
||||
- `ms.security.browser.html` 自定义登录页面的地址(不一定必须参数)
|
||||
- `ms.security.browser.signUpUrl` 第三方登录时用户不存在的时候跳转的注册页面或绑定页面 (必要参数)
|
||||
- `ms.security.browser.loginType` 身份认证后响应类型`REDIRECT`页面形式响应,`JSON`, json形式的响应
|
||||
- `ms.security.browser.rememberMeSeconds` 记住我的时间,单位是秒,默认的时间是3600秒,`remember-me`参数,值为`true`
|
||||
- `/authentication/require`处理身份认证跳转的接口地址
|
||||
- 需要注入`userServiceImpl`获取用户详细信息的bean (必须参数)
|
||||
- 需要注入数据源`masterDataSource`用于记住我功能的bean,记住我功能需要在登录的时候传入(必须参数)
|
||||
### 第三方方登录的配置
|
||||
- 使用这个模块的时候需要借助providerSignInUtils.doPostSignUp();方法实现一个接口将用户的注册或绑定信息插入到数据表中,这里的注
|
||||
册指的是创建本系统自己的用户体系,第三方的用户体系springSocial会自动帮助我们创建。并将url配置可以直接访问
|
||||
- 第三方登陆时,当用户不存在的时候,如果想要系统自动注册一个用户,不进行跳转的话。声明一个类似于demo项目中DemoConnectionSignUp
|
||||
样的组件进行自己用户体系的注册。这里的注册指的是创建本系统自己的用户体系,第三方的用户体系springSocial会自动帮助我们创建。授权后
|
||||
直接跳转到域名首页
|
||||
### session管理
|
||||
- session过期的处理 @GetMapping("/session/invalid")
|
||||
- 用户并发登录的处理MsExpiredSessionStrategy
|
||||
|
||||
+1
@@ -6,4 +6,5 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
public class BootStarterSecurityBrowserApplication {
|
||||
|
||||
}
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.micro.fast.security.browser;
|
||||
|
||||
import com.micro.fast.security.core.validate.code.ValidateCodeRepository;
|
||||
import com.micro.fast.security.core.validate.code.pojo.ValidateCode;
|
||||
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
|
||||
import org.springframework.social.connect.web.SessionStrategy;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import static com.micro.fast.security.core.validate.code.processor.ValidateCodeProcessor.SESSION_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 浏览器存储验证码的策略
|
||||
* @author lsy
|
||||
*/
|
||||
@Component
|
||||
public class BrowserValidateCodeRepository implements ValidateCodeRepository {
|
||||
|
||||
/**
|
||||
* 操作session的工具类
|
||||
*/
|
||||
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
|
||||
|
||||
@Override
|
||||
public void save(ServletWebRequest request, ValidateCode code, String validateCodeType) {
|
||||
//向session中存储验证码
|
||||
sessionStrategy.setAttribute(request
|
||||
,SESSION_KEY_PREFIX+validateCodeType.toUpperCase()
|
||||
,code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidateCode get(ServletWebRequest request, String validateCodeType) {
|
||||
ValidateCode validateCode = (ValidateCode) (sessionStrategy.getAttribute(request, SESSION_KEY_PREFIX + validateCodeType.toUpperCase()));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(ServletWebRequest request, String validateCodeType) {
|
||||
sessionStrategy.removeAttribute(request, SESSION_KEY_PREFIX + validateCodeType.toUpperCase());
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.browser.config;
|
||||
|
||||
import com.micro.fast.security.browser.session.MsExpiredSessionStrategy;
|
||||
import com.micro.fast.security.browser.session.MsInvalidSessionStrategy;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
|
||||
|
||||
|
||||
/**
|
||||
* session过期处理类
|
||||
* @author lsy
|
||||
*/
|
||||
@Configuration
|
||||
public class BrowserSecurityBeanConfig {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(InvalidSessionStrategy.class)
|
||||
public InvalidSessionStrategy invalidSessionStrategy(){
|
||||
return new MsInvalidSessionStrategy(securityProperties.getSession().getSessionInvalidUrl());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SessionInformationExpiredStrategy.class)
|
||||
public SessionInformationExpiredStrategy sessionInformationExpiredStrategy(){
|
||||
return new MsExpiredSessionStrategy(securityProperties.getSession().getSessionInvalidUrl());
|
||||
}
|
||||
|
||||
}
|
||||
+48
-15
@@ -3,6 +3,7 @@ package com.micro.fast.security.browser.config;
|
||||
import com.micro.fast.security.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig;
|
||||
import com.micro.fast.security.core.master.SecurityConstants;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.core.validate.code.ValidateCodeRepository;
|
||||
import com.micro.fast.security.core.validate.code.filter.ValidateCodeFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -11,15 +12,15 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
|
||||
import org.springframework.social.security.SpringSocialConfigurer;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
@@ -43,6 +44,13 @@ public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
//todo 待优化,不唯一,应从启动参数中获取
|
||||
public static final String DRUID = "/druid/*";
|
||||
|
||||
@Autowired
|
||||
private InvalidSessionStrategy invalidSessionStrategy;
|
||||
|
||||
@Autowired
|
||||
private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;
|
||||
|
||||
/**
|
||||
* 注入身份认证框架配置参数对象
|
||||
*/
|
||||
@@ -71,6 +79,8 @@ public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
private ValidateCodeRepository validateCodeRepository;
|
||||
|
||||
/**
|
||||
* 短信验证码登录的配置
|
||||
@@ -78,6 +88,9 @@ public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
|
||||
|
||||
@Autowired
|
||||
private SpringSocialConfigurer springSocialConfigurer;
|
||||
|
||||
/**
|
||||
* 自定义身份认证配置
|
||||
*
|
||||
@@ -90,6 +103,7 @@ public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
|
||||
validateCodeFilter.setAuthenticationFailureHandler(msAuthenticationFailureHandler);
|
||||
validateCodeFilter.setSecurityProperties(securityProperties);
|
||||
validateCodeFilter.setValidateCodeRepository(validateCodeRepository);
|
||||
validateCodeFilter.afterPropertiesSet();
|
||||
http
|
||||
//添加自定义的验证码处理器,在用户密码校验过滤器之前
|
||||
@@ -114,6 +128,24 @@ public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
//设置获取用户信息后登录的方式
|
||||
.userDetailsService(userDetailsService)
|
||||
.and()
|
||||
// session管理的配置
|
||||
.sessionManagement()
|
||||
//session过期处理的类
|
||||
.invalidSessionStrategy(invalidSessionStrategy)
|
||||
//session存在的最大数量
|
||||
.maximumSessions(securityProperties.getSession().getMaximumSessions())
|
||||
//设置是否允许第二个用户登录,如果不阻止的话,会替换掉上一个登录用户
|
||||
.maxSessionsPreventsLogin(securityProperties.getSession().isMaxSessionPreventsLogin())
|
||||
//设置session被替换掉时处理逻辑,是定义session过期的时候处理的url
|
||||
.expiredSessionStrategy(sessionInformationExpiredStrategy)
|
||||
.and()
|
||||
.and()
|
||||
.logout()
|
||||
.logoutUrl("/logout")
|
||||
.logoutSuccessUrl("自定义退出登录后访问的url") //如果不配置的话就会跳向登录处理url
|
||||
.deleteCookies("删除浏览器上指定的cookie")
|
||||
// .logoutSuccessHandler() 定义登出成功处理器,并做一定的处理。这个登出处理器会覆盖掉logoutSuccessUrl的配置
|
||||
.and()
|
||||
.authorizeRequests()//下面是权限配置
|
||||
//当访问这个页面的时候不进行身份认证,对用户自定义的登录页也不进行权限认证
|
||||
.antMatchers(SecurityConstants.MOBILE_LOGIN_PROCESSOR_URI
|
||||
@@ -121,7 +153,14 @@ public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
, SecurityConstants.GET_VALIDATE_CODE_URI
|
||||
, SecurityConstants.LOGIN_PAGE
|
||||
, DRUID
|
||||
, securityProperties.getBrowser().getHtml())
|
||||
, securityProperties.getBrowser().getHtml()
|
||||
//upms系统用户注册
|
||||
, "/upms/user"
|
||||
//用户注册或绑定的页面
|
||||
,securityProperties.getBrowser().getSignUpUrl()
|
||||
,"处理用户的注册绑定请求的url"
|
||||
//处理session请求的url
|
||||
,"/session/invalid")
|
||||
.permitAll()
|
||||
//任何请求
|
||||
.anyRequest()
|
||||
@@ -131,23 +170,17 @@ public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
//暂时关闭掉跨站请求攻击防护
|
||||
.csrf().disable()
|
||||
//应用短信验证码登录的逻辑
|
||||
.apply(smsCodeAuthenticationSecurityConfig);
|
||||
.apply(smsCodeAuthenticationSecurityConfig)
|
||||
.and()
|
||||
//应用springSocial的配置,在接收到特殊的请求的时候进行社交登录.例如 /auth/qq 其中/auth是默认的前缀,qq是providerId
|
||||
.apply(springSocialConfigurer);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义身份认证红密码的加密解密类的bean
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用于记住我功能
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
|
||||
+41
@@ -1,5 +1,6 @@
|
||||
package com.micro.fast.security.browser.controller;
|
||||
|
||||
import com.micro.fast.security.core.pojo.SocialUserInfo;
|
||||
import com.micro.fast.security.core.master.SecurityConstants;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -12,16 +13,23 @@ import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.social.connect.Connection;
|
||||
import org.springframework.social.connect.web.ProviderSignInUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 自定义登录认证处理方式,当触发登录认证的时候跳转到这里
|
||||
* @author lsy
|
||||
*/
|
||||
@RestController
|
||||
public class BrowserSecurityController {
|
||||
@@ -29,6 +37,9 @@ public class BrowserSecurityController {
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Autowired
|
||||
private ProviderSignInUtils providerSignInUtils;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BrowserSecurityController.class);
|
||||
|
||||
private static final String REQUEST_URL_SUFFIX = ".html";
|
||||
@@ -60,5 +71,35 @@ public class BrowserSecurityController {
|
||||
//如果是异步请求的话就放回404状态码和提示信息
|
||||
return "未进行身份认证";
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用providerSignInUtils工具类从session中获取用户第三方登录的信息,这些信息是在第三方
|
||||
* 登录跳转到注册页面之前的时候放入到session中的
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/social/user")
|
||||
public SocialUserInfo getSocialUserInfo(HttpServletRequest request){
|
||||
SocialUserInfo socialUserInfo = new SocialUserInfo();
|
||||
|
||||
Connection<?> connectionFromSession = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
|
||||
socialUserInfo.setProviderId(connectionFromSession.getKey().getProviderId());
|
||||
socialUserInfo.setProviderUserId(connectionFromSession.getKey().getProviderUserId());
|
||||
socialUserInfo.setHeadingUrl(connectionFromSession.getDisplayName());
|
||||
socialUserInfo.setHeadingUrl(connectionFromSession.getImageUrl());
|
||||
return socialUserInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理session过期
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/session/invalid")
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
public Map sessionInvalid(){
|
||||
HashMap<String, String> result = new HashMap<>();
|
||||
result.put("msg","登录时间过长,请重新登录");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package com.micro.fast.security.browser.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.micro.fast.security.core.reponse.ServerResponse;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
*/
|
||||
public class AbstractSessionStrategy {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
/**
|
||||
* 跳转的url
|
||||
*/
|
||||
private String destinationUrl;
|
||||
/**
|
||||
* 重定向策略
|
||||
*/
|
||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||
/**
|
||||
* 跳转前是否创建新的session
|
||||
*/
|
||||
private boolean createNewSession = true;
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param invalidSessionUrl
|
||||
*/
|
||||
public AbstractSessionStrategy(String invalidSessionUrl) {
|
||||
Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'");
|
||||
this.destinationUrl = invalidSessionUrl;
|
||||
}
|
||||
|
||||
|
||||
protected void onSessionInvalid(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
||||
if (createNewSession) {
|
||||
request.getSession();
|
||||
}
|
||||
|
||||
String sourceUrl = request.getRequestURI();
|
||||
String targetUrl;
|
||||
|
||||
if (StringUtils.endsWithIgnoreCase(sourceUrl, ".html")) {
|
||||
targetUrl = destinationUrl+".html";
|
||||
logger.info("session失效,跳转到"+targetUrl);
|
||||
redirectStrategy.sendRedirect(request, response, targetUrl);
|
||||
}else{
|
||||
String message = "session已失效";
|
||||
if(isConcurrency()){
|
||||
message = message + ",有可能是并发登录导致的";
|
||||
}
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write(objectMapper.writeValueAsString(ServerResponse.errorMsg(message)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* session失效是否是并发导致的
|
||||
* @return
|
||||
*/
|
||||
protected boolean isConcurrency() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a new session should be created before redirecting (to
|
||||
* avoid possible looping issues where the same session ID is sent with the
|
||||
* redirected request). Alternatively, ensure that the configured URL does
|
||||
* not pass through the {@code SessionManagementFilter}.
|
||||
*
|
||||
* @param createNewSession
|
||||
* defaults to {@code true}.
|
||||
*/
|
||||
public void setCreateNewSession(boolean createNewSession) {
|
||||
this.createNewSession = createNewSession;
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.browser.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.security.web.session.SessionInformationExpiredEvent;
|
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
*/
|
||||
public class MsExpiredSessionStrategy extends AbstractSessionStrategy implements SessionInformationExpiredStrategy {
|
||||
|
||||
public MsExpiredSessionStrategy(String invalidSessionUrl) {
|
||||
super(invalidSessionUrl);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
|
||||
onSessionInvalid(event.getRequest(), event.getResponse());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isConcurrency() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.browser.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lsy
|
||||
*/
|
||||
@Component
|
||||
public class MsInvalidSessionStrategy extends AbstractSessionStrategy implements InvalidSessionStrategy {
|
||||
|
||||
public MsInvalidSessionStrategy(String invalidSessionUrl) {
|
||||
super(invalidSessionUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException {
|
||||
onSessionInvalid(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,3 +23,25 @@
|
||||
- 短信登录的接口为`/authentication/mobile`,传递参数为`smsCode`和`mobile`
|
||||
- `ms.security.createRememberTable` 是否创建记住我表结构默认为`true`
|
||||
|
||||
### 本项目提供的第三方登录的配置
|
||||
qq 第三方登录
|
||||
- `ms.security.social.qq.appId` qq互联注册的id
|
||||
- `ms.security.social.qq.appSecret` qq互联注册的密码
|
||||
- `ms.security.social.qq.providerId` 第三方登录的标识,例如qq为`qq`登陆默认
|
||||
- `ms.security.social.filterProcessUrl` 进行第三方登录处理的基础url前缀,这个前缀会和providerId进行拼接来进行明确
|
||||
的第三方登录,这个地址也是从第三方应用携带授权码返回的时候进行处理的地址,如果没有授权码就重定向到第三方登录
|
||||
weChat 第三方登录
|
||||
- `ms.security.social.weChat.appId` 类比qq登录
|
||||
- `ms.security.social.weChat.appSecret` 类比qq登录
|
||||
- `ms.security.social.weChat.providerId` 类比qq登录
|
||||
### 绑定,解绑第三方用户
|
||||
- 查询已绑定的用户,请求`/connect`接口即可,要求当前登录的用户放入session中的是SocialUser的对象,能获取到getId
|
||||
- 绑定用户就是发起post请求`/connect/providerId`即可.可以使用自定义名称为`qqConnectedView`或`weChatConnectedView`的bean来自定义视图
|
||||
- 解除绑定的时候,发送delete请求`/connect/providerId`删除社交登录的信息即可。返回的视图和绑定的时候使用相同的视图,在视图里面做逻辑判断
|
||||
### seesion管理
|
||||
- `ms.security.session.maximumSessions`同一个系统中的最大session数,默认1
|
||||
- `ms.security.session.maxSessionPreventsLogin`达到最大session时,是否阻止新的登录请求,默认为false,不阻止,新的登录将老的登
|
||||
录失效掉
|
||||
- `ms.security.session.sessionInvalidUrl`session失效的时候跳转的地址
|
||||
### 退出登录
|
||||
- 默认的退出登录请求的url是`/logout`,退出登录的时候会删除用户session中的信息和记住我存入数据库中的信息然后默认跳转到登录的url
|
||||
|
||||
+13
@@ -1,9 +1,13 @@
|
||||
package com.micro.fast.security.core;
|
||||
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
@@ -12,5 +16,14 @@ import org.springframework.context.annotation.Configuration;
|
||||
@ComponentScan
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
public class BootStarterSecurityCoreApplication {
|
||||
/**
|
||||
* 定义身份认证红密码的加密解密类的bean
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.authentication.mobile;
|
||||
+12
-1
@@ -8,7 +8,10 @@ import com.micro.fast.security.core.browser.constant.LoginType;
|
||||
* @author lsy
|
||||
*/
|
||||
public class BrowserProperties {
|
||||
|
||||
/**
|
||||
* 第三方登录时用户不存在的时候跳转的注册页面
|
||||
*/
|
||||
private String signUpUrl = "/ms-signUp.html";
|
||||
/**
|
||||
* 用户自自定义的登录页面,不能为空
|
||||
*/
|
||||
@@ -47,4 +50,12 @@ public class BrowserProperties {
|
||||
public void setLoginType(LoginType loginType) {
|
||||
this.loginType = loginType;
|
||||
}
|
||||
|
||||
public String getSignUpUrl() {
|
||||
return signUpUrl;
|
||||
}
|
||||
|
||||
public void setSignUpUrl(String signUpUrl) {
|
||||
this.signUpUrl = signUpUrl;
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.browser;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.micro.fast.security.core.master;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
*/
|
||||
public class OAuth2ClientProperties {
|
||||
private String clientId;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private int accessTokenValiditySeconds;
|
||||
|
||||
private String authorizedGrantTypes;
|
||||
|
||||
private String scopes;
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public int getAccessTokenValiditySeconds() {
|
||||
return accessTokenValiditySeconds;
|
||||
}
|
||||
|
||||
public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
|
||||
this.accessTokenValiditySeconds = accessTokenValiditySeconds;
|
||||
}
|
||||
|
||||
public String getAuthorizedGrantTypes() {
|
||||
return authorizedGrantTypes;
|
||||
}
|
||||
|
||||
public void setAuthorizedGrantTypes(String authorizedGrantTypes) {
|
||||
this.authorizedGrantTypes = authorizedGrantTypes;
|
||||
}
|
||||
|
||||
public String getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
public void setScopes(String scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.micro.fast.security.core.master;
|
||||
|
||||
/**
|
||||
* aouth2属性配置
|
||||
* @author lsy
|
||||
*/
|
||||
public class OAuth2Properties {
|
||||
|
||||
private OAuth2ClientProperties[] clients = new OAuth2ClientProperties[]{};
|
||||
|
||||
private String jwtSigningKey = "msjwt";
|
||||
|
||||
private String storeType = "jwt";
|
||||
|
||||
public String getJwtSigningKey() {
|
||||
return jwtSigningKey;
|
||||
}
|
||||
|
||||
public void setJwtSigningKey(String jwtSigningKey) {
|
||||
this.jwtSigningKey = jwtSigningKey;
|
||||
}
|
||||
|
||||
public OAuth2ClientProperties[] getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public void setClients(OAuth2ClientProperties[] clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
}
|
||||
+19
@@ -36,4 +36,23 @@ public interface SecurityConstants {
|
||||
*/
|
||||
String GET_VALIDATE_CODE_PREFIX = "/code/";
|
||||
|
||||
/**
|
||||
* session失效的处理地址
|
||||
*/
|
||||
String DEFAULT_SESSION_INVALID_URL = "/ms-session-invalid.html";
|
||||
|
||||
/**
|
||||
* 获取openId的key
|
||||
*/
|
||||
String DEFAULT_PARAMETER_NAME_OPNEID = "openid";
|
||||
|
||||
/**
|
||||
* 获取providerId的key
|
||||
*/
|
||||
String DEFAULT_PARAMETER_NAME_PROVIDERID = "providerId";
|
||||
|
||||
/**
|
||||
* openID获取access_token的步骤
|
||||
*/
|
||||
String DEFAULT_LOGIN_PROCESS_URL_OPENID = "/authentication/openId";
|
||||
}
|
||||
|
||||
+31
@@ -1,6 +1,7 @@
|
||||
package com.micro.fast.security.core.master;
|
||||
|
||||
import com.micro.fast.security.core.browser.config.param.BrowserProperties;
|
||||
import com.micro.fast.security.core.social.SocialProperties;
|
||||
import com.micro.fast.security.core.validate.code.config.param.ValidateCodeProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -25,6 +26,20 @@ public class SecurityProperties {
|
||||
|
||||
private Boolean createRememberTable = true;
|
||||
|
||||
private SocialProperties social = new SocialProperties();
|
||||
|
||||
private SessionProperties session = new SessionProperties();
|
||||
|
||||
private OAuth2Properties oauth2 = new OAuth2Properties();
|
||||
|
||||
public SessionProperties getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void setSession(SessionProperties session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public Boolean getCreateRememberTable() {
|
||||
return createRememberTable;
|
||||
}
|
||||
@@ -48,4 +63,20 @@ public class SecurityProperties {
|
||||
public void setCode(ValidateCodeProperties code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public SocialProperties getSocial() {
|
||||
return social;
|
||||
}
|
||||
|
||||
public void setSocial(SocialProperties social) {
|
||||
this.social = social;
|
||||
}
|
||||
|
||||
public OAuth2Properties getOauth2() {
|
||||
return oauth2;
|
||||
}
|
||||
|
||||
public void setOauth2(OAuth2Properties oauth2) {
|
||||
this.oauth2 = oauth2;
|
||||
}
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.micro.fast.security.core.master;
|
||||
|
||||
/**
|
||||
* session处理的配置
|
||||
* @author lsy
|
||||
*/
|
||||
public class SessionProperties {
|
||||
/**
|
||||
* 同一个系统中的最大session数,默认1
|
||||
*/
|
||||
private int maximumSessions = 1;
|
||||
|
||||
/**
|
||||
* 达到最大session时,是否阻止新的登录请求,默认为false,不阻止,新的登录将老的登录
|
||||
* 失效掉
|
||||
*/
|
||||
private boolean maxSessionPreventsLogin = false;
|
||||
|
||||
/**
|
||||
* session失效的时候跳转的地址
|
||||
*/
|
||||
private String sessionInvalidUrl = SecurityConstants.DEFAULT_SESSION_INVALID_URL;
|
||||
|
||||
public int getMaximumSessions() {
|
||||
return maximumSessions;
|
||||
}
|
||||
|
||||
public void setMaximumSessions(int maximumSessions) {
|
||||
this.maximumSessions = maximumSessions;
|
||||
}
|
||||
|
||||
public boolean isMaxSessionPreventsLogin() {
|
||||
return maxSessionPreventsLogin;
|
||||
}
|
||||
|
||||
public void setMaxSessionPreventsLogin(boolean maxSessionPreventsLogin) {
|
||||
this.maxSessionPreventsLogin = maxSessionPreventsLogin;
|
||||
}
|
||||
|
||||
public String getSessionInvalidUrl() {
|
||||
return sessionInvalidUrl;
|
||||
}
|
||||
|
||||
public void setSessionInvalidUrl(String sessionInvalidUrl) {
|
||||
this.sessionInvalidUrl = sessionInvalidUrl;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.master;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.micro.fast.security.core.pojo;
|
||||
|
||||
/**
|
||||
* 第三方登录时用户的信息
|
||||
* @author lsy
|
||||
*/
|
||||
public class SocialUserInfo {
|
||||
/**
|
||||
* 第三方登录的服务商标识
|
||||
*/
|
||||
private String providerId;
|
||||
/**
|
||||
* 用户的第三方id
|
||||
*/
|
||||
private String providerUserId;
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
/**
|
||||
* 用户头像的url
|
||||
*/
|
||||
private String headingUrl;
|
||||
|
||||
public String getProviderId() {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
public void setProviderId(String providerId) {
|
||||
this.providerId = providerId;
|
||||
}
|
||||
|
||||
public String getProviderUserId() {
|
||||
return providerUserId;
|
||||
}
|
||||
|
||||
public void setProviderUserId(String providerUserId) {
|
||||
this.providerUserId = providerUserId;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public String getHeadingUrl() {
|
||||
return headingUrl;
|
||||
}
|
||||
|
||||
public void setHeadingUrl(String headingUrl) {
|
||||
this.headingUrl = headingUrl;
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package com.micro.fast.security.core.reponse;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
/**
|
||||
* 统一服务响应类,方法返回值
|
||||
* 序列化中不包含为空值的字段
|
||||
* 0表示成功,1表示失败
|
||||
* @author lishouyu 18332763730@163.com 2017/10/02
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
public class ServerResponse<T> {
|
||||
/**
|
||||
* 获取数据返回的提示数据字典码
|
||||
*/
|
||||
private Integer code;
|
||||
/**
|
||||
* 获取数据成功或者失败的时候提示的消息
|
||||
*/
|
||||
private String msg;
|
||||
/**
|
||||
* 返回的数据
|
||||
*/
|
||||
T data;
|
||||
|
||||
/**
|
||||
* 所有的构造方法均为私有,不允许外部直接调用构造方法
|
||||
*/
|
||||
public ServerResponse() {
|
||||
|
||||
}
|
||||
|
||||
private ServerResponse(Integer code) {
|
||||
this(code, null, null);
|
||||
}
|
||||
|
||||
private ServerResponse(Integer code, String msg) {
|
||||
this(code, msg, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为了避免T 为string的时候调用的是 上面的构造方法,需要对构造方法进行包装。构造方法设置为私有,不允许外部调用
|
||||
*
|
||||
* @param code 响应代码
|
||||
* @param data 相应数据
|
||||
*/
|
||||
private ServerResponse(Integer code, T data) {
|
||||
this(code, null, data);
|
||||
}
|
||||
|
||||
private ServerResponse(Integer code, String msg, T data) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回代码是小于等于0的时候表示请求数据失败,或者进行验证时失败
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
@JsonIgnore //使其不再json序列化结果之中
|
||||
public boolean isSuccess() {
|
||||
return 0 == this.code;
|
||||
}
|
||||
|
||||
//请求成功
|
||||
public static <T> ServerResponse<T> success() {
|
||||
|
||||
return new ServerResponse<T>(0);
|
||||
}
|
||||
|
||||
public static <T> ServerResponse<T> successMsg(String msg) {
|
||||
|
||||
return new ServerResponse<T>(
|
||||
0,
|
||||
msg);
|
||||
}
|
||||
|
||||
public static <T> ServerResponse<T> successData(T data) {
|
||||
|
||||
return new ServerResponse<T>(0,
|
||||
data);
|
||||
}
|
||||
|
||||
public static <T> ServerResponse<T> successMsgData(String msg, T data) {
|
||||
|
||||
return new ServerResponse<T>(0,
|
||||
msg, data);
|
||||
}
|
||||
|
||||
//请求失败
|
||||
public static <T> ServerResponse<T> error() {
|
||||
return new ServerResponse<T>(1);
|
||||
}
|
||||
|
||||
public static <T> ServerResponse<T> errorMsg(String msg) {
|
||||
return new ServerResponse<T>(1, msg);
|
||||
}
|
||||
|
||||
public static <T> ServerResponse<T> errorCodeMsg(Integer code, String msg) {
|
||||
return new ServerResponse<T>(code, msg);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.micro.fast.security.core.social;
|
||||
|
||||
import org.springframework.web.servlet.view.AbstractView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 拦截请求/connect/providerId返回的/connect/providerId视图的信息,这时候是从微信的页面返回来的。所以要给前台一个页面
|
||||
* @author lsy
|
||||
*/
|
||||
public class MsConnectedView extends AbstractView{
|
||||
@Override
|
||||
protected void renderMergedOutputModel(Map<String, Object> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
|
||||
httpServletResponse.setContentType("text/html;charset=UTF-8");
|
||||
httpServletResponse.getWriter().write("<h3>绑定成功</h3>");
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.micro.fast.security.core.social;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.social.connect.Connection;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.view.AbstractView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 定义一个返回给前端第三方账户绑定情况的视图,拦截请求/connect返回的/connect/status视图的信息,并装换成json格式
|
||||
* @author lsy
|
||||
*/
|
||||
@Component("connect/status")
|
||||
public class MsConnectionStatusView extends AbstractView {
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
|
||||
@Override
|
||||
protected void renderMergedOutputModel(Map<String, Object> map
|
||||
, HttpServletRequest httpServletRequest
|
||||
, HttpServletResponse httpServletResponse)
|
||||
throws Exception {
|
||||
Map<String,List<Connection<?>>> connections = (Map<String,List<Connection<?>>>)map.get("connectionMap");
|
||||
Map<String,Boolean> result = new HashMap<>(5);
|
||||
connections.forEach((key,value)->{
|
||||
result.put(key, CollectionUtils.isNotEmpty(value));
|
||||
});
|
||||
//返回json格式的数据
|
||||
httpServletResponse.setContentType("application/json;charset=UTF-8");
|
||||
httpServletResponse.getWriter().write(objectMapper.writeValueAsString(result));
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.micro.fast.security.core.social;
|
||||
|
||||
import org.springframework.social.security.SocialAuthenticationFilter;
|
||||
import org.springframework.social.security.SpringSocialConfigurer;
|
||||
|
||||
/**
|
||||
* 自定义springSocial配置类,这个bean在SocialConfig中声明
|
||||
* @author lsy
|
||||
*/
|
||||
public class MsSpringSocialConfigurer extends SpringSocialConfigurer{
|
||||
|
||||
/**
|
||||
* 验证登录后处理器
|
||||
*/
|
||||
private SocialAuthenticationFilterPostProcessor socialAuthenticationFilterPostProcessor;
|
||||
/**
|
||||
* 第三方登录的url
|
||||
*/
|
||||
private String filterProcessUrl;
|
||||
|
||||
public MsSpringSocialConfigurer(String filterProcessUrl) {
|
||||
this.filterProcessUrl = filterProcessUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对第三方登录的拦截器进行自定义设置
|
||||
* @param object
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected <T> T postProcess(T object) {
|
||||
SocialAuthenticationFilter filter = (SocialAuthenticationFilter) super.postProcess(object);
|
||||
//设置第三方登录的url
|
||||
filter.setFilterProcessesUrl(this.filterProcessUrl);
|
||||
//如果后处理器不为空,我们就把filter传递给自定义的后处理器
|
||||
if (socialAuthenticationFilterPostProcessor != null){
|
||||
socialAuthenticationFilterPostProcessor.process(filter);
|
||||
}
|
||||
return (T) filter;
|
||||
}
|
||||
|
||||
public SocialAuthenticationFilterPostProcessor getSocialAuthenticationFilterPostProcessor() {
|
||||
return socialAuthenticationFilterPostProcessor;
|
||||
}
|
||||
|
||||
public void setSocialAuthenticationFilterPostProcessor(SocialAuthenticationFilterPostProcessor socialAuthenticationFilterPostProcessor) {
|
||||
this.socialAuthenticationFilterPostProcessor = socialAuthenticationFilterPostProcessor;
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.micro.fast.security.core.social;
|
||||
|
||||
import org.springframework.social.security.SocialAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
*/
|
||||
public interface SocialAuthenticationFilterPostProcessor {
|
||||
void process(SocialAuthenticationFilter socialAuthenticationFilter);
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package com.micro.fast.security.core.social;
|
||||
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.encrypt.Encryptors;
|
||||
import org.springframework.social.config.annotation.EnableSocial;
|
||||
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
|
||||
import org.springframework.social.connect.ConnectionFactoryLocator;
|
||||
import org.springframework.social.connect.ConnectionSignUp;
|
||||
import org.springframework.social.connect.UsersConnectionRepository;
|
||||
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;
|
||||
import org.springframework.social.connect.web.ProviderSignInUtils;
|
||||
import org.springframework.social.security.SpringSocialConfigurer;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* 第三方登录的配置类
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSocial
|
||||
public class SocialConfig extends SocialConfigurerAdapter{
|
||||
|
||||
/**
|
||||
* 使用的数据源
|
||||
*/
|
||||
@Autowired
|
||||
@Qualifier("dynamicDataSource")
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Autowired
|
||||
private ConnectionFactoryLocator connectionFactoryLocator;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ConnectionSignUp connectionSignUp;
|
||||
|
||||
@Autowired(required = false)
|
||||
private SocialAuthenticationFilterPostProcessor socialAuthenticationFilterPostProcessor;
|
||||
/**
|
||||
*
|
||||
* @param connectionFactoryLocator
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
|
||||
//第三参数是加解密的策略,这里指定是不做加解密操作。对插入到数据库中的信息进行加解密
|
||||
JdbcUsersConnectionRepository jdbcUsersConnectionRepository = new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
|
||||
//使用OAuth2.sql创建如果数据表自定义前缀的话在这里声明
|
||||
jdbcUsersConnectionRepository.setTablePrefix("upms_");
|
||||
//如果用户定义了connectionSignUp注册用户的逻辑就放入数据库操作类中默认创建用户
|
||||
if (connectionSignUp !=null){
|
||||
jdbcUsersConnectionRepository.setConnectionSignUp(connectionSignUp);
|
||||
}
|
||||
return jdbcUsersConnectionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* springSocialconfigure的配置的bean
|
||||
* @return
|
||||
*/
|
||||
@Bean("msSpringSocialConfigurer")
|
||||
public SpringSocialConfigurer springSocialConfigurer(){
|
||||
SocialProperties social = securityProperties.getSocial();
|
||||
MsSpringSocialConfigurer msSpringSocialConfigurer = new MsSpringSocialConfigurer(social.getFilterProcessUrl());
|
||||
// 设置第三方登录用户不存在的时候,注册或绑定的页面。如果设置为空则不跳转
|
||||
msSpringSocialConfigurer.signupUrl(securityProperties.getBrowser().getSignUpUrl());
|
||||
//注入后处理器
|
||||
msSpringSocialConfigurer.setSocialAuthenticationFilterPostProcessor(socialAuthenticationFilterPostProcessor);
|
||||
return msSpringSocialConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在第三方登录跳转到的注册页面显示第三方的用户信息
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public ProviderSignInUtils providerSignInUtils(){
|
||||
return new ProviderSignInUtils(connectionFactoryLocator,getUsersConnectionRepository(connectionFactoryLocator));
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.micro.fast.security.core.social;
|
||||
|
||||
import com.micro.fast.security.core.social.qq.QQSocialProperties;
|
||||
import com.micro.fast.security.core.social.wechat.WeChatSocialProperties;
|
||||
|
||||
/**
|
||||
* 第三方登录配置类
|
||||
* @author lsy
|
||||
*/
|
||||
public class SocialProperties {
|
||||
/**
|
||||
* QQ登录配置
|
||||
*/
|
||||
private QQSocialProperties qq = new QQSocialProperties();
|
||||
|
||||
/**
|
||||
* 微信登录配置
|
||||
*/
|
||||
private WeChatSocialProperties weChat = new WeChatSocialProperties();
|
||||
|
||||
/**
|
||||
* 第三方登录拦截url的配置
|
||||
*/
|
||||
private String filterProcessUrl = "/auth";
|
||||
|
||||
public WeChatSocialProperties getWeChat() {
|
||||
return weChat;
|
||||
}
|
||||
|
||||
public void setWeChat(WeChatSocialProperties weChat) {
|
||||
this.weChat = weChat;
|
||||
}
|
||||
|
||||
public QQSocialProperties getQq() {
|
||||
return qq;
|
||||
}
|
||||
|
||||
public void setQq(QQSocialProperties qq) {
|
||||
this.qq = qq;
|
||||
}
|
||||
|
||||
public String getFilterProcessUrl() {
|
||||
return filterProcessUrl;
|
||||
}
|
||||
|
||||
public void setFilterProcessUrl(String filterProcessUrl) {
|
||||
this.filterProcessUrl = filterProcessUrl;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.micro.fast.security.core.social.config;
|
||||
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.core.social.qq.QQSocialProperties;
|
||||
import com.micro.fast.security.core.social.qq.connet.QQConnectionFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.social.SocialAutoConfigurerAdapter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.social.connect.ConnectionFactory;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "ms.security.social.qq",name = "app-id")
|
||||
public class QQAutoConfig extends SocialAutoConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory<?> createConnectionFactory() {
|
||||
QQSocialProperties qq = securityProperties.getSocial().getQq();
|
||||
return new QQConnectionFactory(qq.getProviderId(),qq.getAppId(),qq.getAppSecret());
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.social;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.micro.fast.security.core.social.qq;
|
||||
|
||||
import org.springframework.boot.autoconfigure.social.SocialProperties;
|
||||
|
||||
/**
|
||||
* QQ第三方登录配置对象
|
||||
* @author lsy
|
||||
*/
|
||||
public class QQSocialProperties extends SocialProperties{
|
||||
/**
|
||||
* 第三方登录标识
|
||||
*/
|
||||
private String providerId = "qq";
|
||||
|
||||
public String getProviderId() {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
public void setProviderId(String providerId) {
|
||||
this.providerId = providerId;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.micro.fast.security.core.social.qq.api;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lsy
|
||||
*/
|
||||
public interface QQ {
|
||||
/**
|
||||
* 获取qq用户的信息
|
||||
* @return
|
||||
*/
|
||||
QQUserInfo getQQUserInfo() ;
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package com.micro.fast.security.core.social.qq.api;
|
||||
|
||||
/**
|
||||
* QQ用户信息的实体类
|
||||
* @author lsy
|
||||
*/
|
||||
public class QQUserInfo {
|
||||
|
||||
/**
|
||||
* ret : 0
|
||||
* msg :
|
||||
* nickname : Peter
|
||||
* figureurl : http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/30
|
||||
* figureurl_1 : http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/50
|
||||
* figureurl_2 : http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/100
|
||||
* figureurl_qq_1 : http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/40
|
||||
* figureurl_qq_2 : http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/100
|
||||
* gender : 男
|
||||
* is_yellow_vip : 1
|
||||
* vip : 1
|
||||
* yellow_vip_level : 7
|
||||
* level : 7
|
||||
* is_yellow_year_vip : 1
|
||||
*/
|
||||
|
||||
private int ret;
|
||||
private String openId;
|
||||
private String msg;
|
||||
private String nickname;
|
||||
private String figureurl;
|
||||
private String figureurl_1;
|
||||
private String figureurl_2;
|
||||
private String figureurl_qq_1;
|
||||
private String figureurl_qq_2;
|
||||
private String gender;
|
||||
private String is_yellow_vip;
|
||||
private String vip;
|
||||
private String yellow_vip_level;
|
||||
private String level;
|
||||
private String is_yellow_year_vip;
|
||||
|
||||
public int getRet() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void setRet(int ret) {
|
||||
this.ret = ret;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public String getFigureurl() {
|
||||
return figureurl;
|
||||
}
|
||||
|
||||
public void setFigureurl(String figureurl) {
|
||||
this.figureurl = figureurl;
|
||||
}
|
||||
|
||||
public String getFigureurl_1() {
|
||||
return figureurl_1;
|
||||
}
|
||||
|
||||
public void setFigureurl_1(String figureurl_1) {
|
||||
this.figureurl_1 = figureurl_1;
|
||||
}
|
||||
|
||||
public String getFigureurl_2() {
|
||||
return figureurl_2;
|
||||
}
|
||||
|
||||
public void setFigureurl_2(String figureurl_2) {
|
||||
this.figureurl_2 = figureurl_2;
|
||||
}
|
||||
|
||||
public String getFigureurl_qq_1() {
|
||||
return figureurl_qq_1;
|
||||
}
|
||||
|
||||
public void setFigureurl_qq_1(String figureurl_qq_1) {
|
||||
this.figureurl_qq_1 = figureurl_qq_1;
|
||||
}
|
||||
|
||||
public String getFigureurl_qq_2() {
|
||||
return figureurl_qq_2;
|
||||
}
|
||||
|
||||
public void setFigureurl_qq_2(String figureurl_qq_2) {
|
||||
this.figureurl_qq_2 = figureurl_qq_2;
|
||||
}
|
||||
|
||||
public String getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(String gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public String getIs_yellow_vip() {
|
||||
return is_yellow_vip;
|
||||
}
|
||||
|
||||
public void setIs_yellow_vip(String is_yellow_vip) {
|
||||
this.is_yellow_vip = is_yellow_vip;
|
||||
}
|
||||
|
||||
public String getVip() {
|
||||
return vip;
|
||||
}
|
||||
|
||||
public void setVip(String vip) {
|
||||
this.vip = vip;
|
||||
}
|
||||
|
||||
public String getYellow_vip_level() {
|
||||
return yellow_vip_level;
|
||||
}
|
||||
|
||||
public void setYellow_vip_level(String yellow_vip_level) {
|
||||
this.yellow_vip_level = yellow_vip_level;
|
||||
}
|
||||
|
||||
public String getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(String level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public String getIs_yellow_year_vip() {
|
||||
return is_yellow_year_vip;
|
||||
}
|
||||
|
||||
public String getOpenId() {
|
||||
return openId;
|
||||
}
|
||||
|
||||
public void setOpenId(String openId) {
|
||||
this.openId = openId;
|
||||
}
|
||||
|
||||
public void setIs_yellow_year_vip(String is_yellow_year_vip) {
|
||||
this.is_yellow_year_vip = is_yellow_year_vip;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.micro.fast.security.core.social.qq.api.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.micro.fast.security.core.social.qq.api.QQUserInfo;
|
||||
import com.micro.fast.security.core.social.qq.api.QQ;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
|
||||
import org.springframework.social.oauth2.TokenStrategy;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 获取qq用户信息的实现类,qqApi用于组装QQServiceProvider
|
||||
* @author lsy
|
||||
*/
|
||||
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
|
||||
|
||||
/**
|
||||
* 获取用户openId的字符串
|
||||
*/
|
||||
public static final String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
|
||||
|
||||
/**
|
||||
* 获取qq用户信息的字符串
|
||||
*/
|
||||
public static final String URL_GET_USERINFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";
|
||||
|
||||
/**
|
||||
* 系统在qq互联注册的id
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* qq用户的id
|
||||
*/
|
||||
private String openId;
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public QQImpl(String accessToken,String appId){
|
||||
//将accessToken放在请求的url中
|
||||
super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
|
||||
this.appId = appId;
|
||||
//使用accessToken获取用户的openId
|
||||
String url = String.format(URL_GET_OPENID,accessToken);
|
||||
String result = getRestTemplate().getForObject(url, String.class);
|
||||
System.out.println(result);
|
||||
this.openId = StringUtils.substringBetween(result,"\"openid\":\"","\"}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public QQUserInfo getQQUserInfo() {
|
||||
//获取qq用户的信息对象
|
||||
String url = String.format(URL_GET_USERINFO, appId, openId);
|
||||
String result = getRestTemplate().getForObject(url, String.class);
|
||||
QQUserInfo qqUserInfo = null;
|
||||
try {
|
||||
qqUserInfo = objectMapper.readValue(result, QQUserInfo.class);
|
||||
//将获取的openId设置在用户里面
|
||||
qqUserInfo.setOpenId(openId);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("获取用户信息失败");
|
||||
}
|
||||
return qqUserInfo;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.micro.fast.security.core.social.qq.config;
|
||||
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.core.social.MsConnectedView;
|
||||
import com.micro.fast.security.core.social.qq.QQSocialProperties;
|
||||
import com.micro.fast.security.core.social.qq.connet.QQConnectionFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.social.SocialAutoConfigurerAdapter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.social.connect.ConnectionFactory;
|
||||
import org.springframework.web.servlet.View;
|
||||
|
||||
/**
|
||||
* @author lsy
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "ms.security.social.qq",name = "app-id")
|
||||
public class QQAutoConfig extends SocialAutoConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory<?> createConnectionFactory() {
|
||||
QQSocialProperties qq = securityProperties.getSocial().getQq();
|
||||
return new QQConnectionFactory(qq.getProviderId(),qq.getAppId(),qq.getAppSecret());
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义绑定成功返回的视图,和返回失败时候的视图
|
||||
*/
|
||||
@Bean({"connect/qqConnected","connect/qqConnect"})
|
||||
@ConditionalOnMissingBean(name = "qqConnectedView")
|
||||
public View qqConnectedView(){
|
||||
MsConnectedView msConnectedView = new MsConnectedView();
|
||||
return msConnectedView;
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.micro.fast.security.core.social.qq.connet;
|
||||
|
||||
import com.micro.fast.security.core.social.qq.api.QQ;
|
||||
import com.micro.fast.security.core.social.qq.api.QQUserInfo;
|
||||
import org.springframework.social.connect.ApiAdapter;
|
||||
import org.springframework.social.connect.ConnectionValues;
|
||||
import org.springframework.social.connect.UserProfile;
|
||||
|
||||
/**
|
||||
* QQ的ApiAdapter ,用于组装QQConnectionFactory
|
||||
* @author lsy
|
||||
*/
|
||||
public class QQAdapter implements ApiAdapter<QQ> {
|
||||
|
||||
/**
|
||||
* 测试当前的qqApi是否可用
|
||||
* @param qq
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean test(QQ qq) {
|
||||
//默认qq第三方登录永久可用
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置用户的连接信息
|
||||
* @param qq
|
||||
* @param connectionValues
|
||||
*/
|
||||
@Override
|
||||
public void setConnectionValues(QQ qq, ConnectionValues connectionValues) {
|
||||
QQUserInfo qqUserInfo = qq.getQQUserInfo();
|
||||
//设置用户的昵称
|
||||
connectionValues.setDisplayName(qqUserInfo.getNickname());
|
||||
//qq用户40*40的头像
|
||||
connectionValues.setImageUrl(qqUserInfo.getFigureurl_qq_1());
|
||||
//设置用户的主页
|
||||
connectionValues.setProfileUrl(null);
|
||||
//服务商提供的用户的openID
|
||||
connectionValues.setProviderUserId(qqUserInfo.getOpenId());
|
||||
}
|
||||
|
||||
/**
|
||||
* QQ没有此功能
|
||||
* @param qq
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserProfile fetchUserProfile(QQ qq) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* qq没有此功能
|
||||
* @param qq
|
||||
* @param s
|
||||
*/
|
||||
@Override
|
||||
public void updateStatus(QQ qq, String s) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.micro.fast.security.core.social.qq.connet;
|
||||
|
||||
import com.micro.fast.security.core.social.qq.api.QQ;
|
||||
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
|
||||
|
||||
/**
|
||||
* QQ授权连接工厂对象
|
||||
* @author lsy
|
||||
*/
|
||||
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {
|
||||
/**
|
||||
* 使用连接工厂创建连接
|
||||
* @param providerId 服务提供商的唯一标识
|
||||
* @param appId 应用系统登录第三方授权的用户名
|
||||
* @param appSecret 应用系统登录第三方授权的密码
|
||||
*/
|
||||
public QQConnectionFactory(String providerId
|
||||
,String appId,String appSecret) {
|
||||
super(providerId,new QQServiceProvider(appId,appSecret),new QQAdapter());
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.micro.fast.security.core.social.qq.connet;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.social.oauth2.AccessGrant;
|
||||
import org.springframework.social.oauth2.OAuth2Template;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 处理qq第三方登录返回授权码Template,默认的OAuth2Template创建的template不能处理text/html的response
|
||||
* 这个类在QQServiceProvider类中被实例化
|
||||
* @author lsy
|
||||
*/
|
||||
public class QQOAuth2Template extends OAuth2Template{
|
||||
|
||||
public QQOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
|
||||
super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
|
||||
//默认useParametersForClientAuthentication属性是false的,为false的话不会携带应用的appId和app_secret
|
||||
setUseParametersForClientAuthentication(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对获取access_Token的过程进行自定义
|
||||
* @param accessTokenUrl
|
||||
* @param parameters
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
|
||||
String responseStr = getRestTemplate().postForObject(accessTokenUrl,parameters,String.class);
|
||||
String[] split = responseStr.split("&");
|
||||
String accessToken = StringUtils.substringAfterLast(split[0],"=");
|
||||
String expireIn = StringUtils.substringAfterLast(split[1],"=");
|
||||
String refreshToken = StringUtils.substringAfterLast(split[2],"=");
|
||||
return new AccessGrant(accessToken,null,refreshToken,Long.valueOf(expireIn));
|
||||
}
|
||||
|
||||
/**
|
||||
* 对qq返回的授权码进行处理
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected RestTemplate createRestTemplate() {
|
||||
RestTemplate restTemplate = super.createRestTemplate();
|
||||
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
|
||||
//添加处理text/html的转换器
|
||||
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
|
||||
return restTemplate;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.micro.fast.security.core.social.qq.connet;
|
||||
|
||||
import com.micro.fast.security.core.social.qq.api.QQ;
|
||||
import com.micro.fast.security.core.social.qq.api.impl.QQImpl;
|
||||
import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider;
|
||||
import org.springframework.social.oauth2.OAuth2Template;
|
||||
|
||||
/**
|
||||
* 获取QQ用户信息服务的提供方类,用于组装QQConnectionFactory
|
||||
* @author lsy
|
||||
*/
|
||||
public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {
|
||||
|
||||
/**
|
||||
* qq分配给应用的id
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 认证服务器地址
|
||||
*/
|
||||
public static final String URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize";
|
||||
|
||||
/**
|
||||
* accessToken的获取地址
|
||||
*/
|
||||
public static final String URL_ACCESS_TOKEN = "https://graph.qq.com/oauth2.0/token";
|
||||
|
||||
/**
|
||||
* qq互联会给每一个应用分配一个appId和一个appSecret,相当于appd的id和密码
|
||||
* @param appId 应用的id
|
||||
* @param appSecret 应用的密码
|
||||
*/
|
||||
public QQServiceProvider(String appId,String appSecret) {
|
||||
//使用自定义的OAuthTemplate
|
||||
super(new QQOAuth2Template(appId,appSecret,URL_AUTHORIZE,URL_ACCESS_TOKEN));
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取用户信息的对象
|
||||
* @param accessToken 会由抽象类传入
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public QQ getApi(String accessToken) {
|
||||
return new QQImpl(accessToken ,this.appId);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.micro.fast.security.core.social.wechat;
|
||||
|
||||
|
||||
import org.springframework.boot.autoconfigure.social.SocialProperties;
|
||||
|
||||
/**
|
||||
* weChat第三方登录配置类
|
||||
* @author lsy
|
||||
*/
|
||||
public class WeChatSocialProperties extends SocialProperties {
|
||||
/**
|
||||
* 第三方登录标识
|
||||
*/
|
||||
private String providerId = "weChat";
|
||||
|
||||
public String getProviderId() {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
public void setProviderId(String providerId) {
|
||||
this.providerId = providerId;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.micro.fast.security.core.social.wechat.api;
|
||||
|
||||
/**
|
||||
* weChat 获取用户信息的接口
|
||||
* @author lsy
|
||||
*/
|
||||
public interface WeChat {
|
||||
/**
|
||||
* 获取微信登录用户的信息
|
||||
* @return
|
||||
*/
|
||||
WeChatUserInfo getWeChatUserInfo(String openId);
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
package com.micro.fast.security.core.social.wechat.api;
|
||||
|
||||
/**
|
||||
* 微信登录用户的第三方信息
|
||||
* @author lsy
|
||||
*/
|
||||
public class WeChatUserInfo {
|
||||
/**
|
||||
* 普通用户的标识,对当前开发者帐号唯一
|
||||
*/
|
||||
private String openid;
|
||||
/**
|
||||
* 普通用户昵称
|
||||
*/
|
||||
private String nickname;
|
||||
/**
|
||||
* 语言
|
||||
*/
|
||||
private String language;
|
||||
/**
|
||||
* 普通用户性别,1为男性,2为女性
|
||||
*/
|
||||
private String sex;
|
||||
/**
|
||||
* 普通用户个人资料填写的省份
|
||||
*/
|
||||
private String province;
|
||||
/**
|
||||
* 普通用户个人资料填写的城市
|
||||
*/
|
||||
private String city;
|
||||
/**
|
||||
* 国家,如中国为CN
|
||||
*/
|
||||
private String country;
|
||||
/**
|
||||
* 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
|
||||
*/
|
||||
private String headimgurl;
|
||||
/**
|
||||
* 用户特权信息,json数组,如微信沃卡用户为(chinaunicom)
|
||||
*/
|
||||
private String[] privilege;
|
||||
/**
|
||||
* 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。
|
||||
*/
|
||||
private String unionid;
|
||||
|
||||
/**
|
||||
* @return the openid
|
||||
*/
|
||||
public String getOpenid() {
|
||||
return openid;
|
||||
}
|
||||
/**
|
||||
* @param openid the openid to set
|
||||
*/
|
||||
public void setOpenid(String openid) {
|
||||
this.openid = openid;
|
||||
}
|
||||
/**
|
||||
* @return the nickname
|
||||
*/
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
/**
|
||||
* @param nickname the nickname to set
|
||||
*/
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
/**
|
||||
* @return the sex
|
||||
*/
|
||||
public String getSex() {
|
||||
return sex;
|
||||
}
|
||||
/**
|
||||
* @param sex the sex to set
|
||||
*/
|
||||
public void setSex(String sex) {
|
||||
this.sex = sex;
|
||||
}
|
||||
/**
|
||||
* @return the province
|
||||
*/
|
||||
public String getProvince() {
|
||||
return province;
|
||||
}
|
||||
/**
|
||||
* @param province the province to set
|
||||
*/
|
||||
public void setProvince(String province) {
|
||||
this.province = province;
|
||||
}
|
||||
/**
|
||||
* @return the city
|
||||
*/
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
/**
|
||||
* @param city the city to set
|
||||
*/
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
/**
|
||||
* @return the country
|
||||
*/
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
/**
|
||||
* @param country the country to set
|
||||
*/
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
/**
|
||||
* @return the headimgurl
|
||||
*/
|
||||
public String getHeadimgurl() {
|
||||
return headimgurl;
|
||||
}
|
||||
/**
|
||||
* @param headimgurl the headimgurl to set
|
||||
*/
|
||||
public void setHeadimgurl(String headimgurl) {
|
||||
this.headimgurl = headimgurl;
|
||||
}
|
||||
/**
|
||||
* @return the privilege
|
||||
*/
|
||||
public String[] getPrivilege() {
|
||||
return privilege;
|
||||
}
|
||||
/**
|
||||
* @param privilege the privilege to set
|
||||
*/
|
||||
public void setPrivilege(String[] privilege) {
|
||||
this.privilege = privilege;
|
||||
}
|
||||
/**
|
||||
* @return the unionid
|
||||
*/
|
||||
public String getUnionid() {
|
||||
return unionid;
|
||||
}
|
||||
/**
|
||||
* @param unionid the unionid to set
|
||||
*/
|
||||
public void setUnionid(String unionid) {
|
||||
this.unionid = unionid;
|
||||
}
|
||||
/**
|
||||
* @return the language
|
||||
*/
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
/**
|
||||
* @param language the language to set
|
||||
*/
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.micro.fast.security.core.social.wechat.api.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.micro.fast.security.core.social.wechat.api.WeChat;
|
||||
import com.micro.fast.security.core.social.wechat.api.WeChatUserInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
|
||||
import org.springframework.social.oauth2.TokenStrategy;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* weChat获取用户信息接口的实现类,微信实现的并不是标准的OAuth2协议
|
||||
* @author lsy
|
||||
*/
|
||||
public class WeChatImpl extends AbstractOAuth2ApiBinding implements WeChat {
|
||||
|
||||
/**
|
||||
* 微信接口获取用户信息的url
|
||||
*/
|
||||
private static final String URL_GET_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?openid=";
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* @param accessToken
|
||||
*/
|
||||
public WeChatImpl(String accessToken) {
|
||||
super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认注册的StringHttpMessageConverter字符集为ISO-8859-1,而微信返回的是UTF-8的,所以覆盖了原来的方法。
|
||||
*/
|
||||
@Override
|
||||
protected List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
List<HttpMessageConverter<?>> messageConverters = super.getMessageConverters();
|
||||
messageConverters.remove(0);
|
||||
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
|
||||
return messageConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeChatUserInfo getWeChatUserInfo(String openId) {
|
||||
String url = URL_GET_USER_INFO + openId;
|
||||
String response = getRestTemplate().getForObject(url, String.class);
|
||||
if(StringUtils.contains(response, "errcode")) {
|
||||
return null;
|
||||
}
|
||||
WeChatUserInfo profile = null;
|
||||
try {
|
||||
profile = objectMapper.readValue(response, WeChatUserInfo.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("获取用户信息失败");
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.social.wechat.api.impl;
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.social.wechat.api;
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.core.social.wechat.config;
|
||||
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.core.social.MsConnectedView;
|
||||
import com.micro.fast.security.core.social.wechat.WeChatSocialProperties;
|
||||
import com.micro.fast.security.core.social.wechat.connet.WeChatConnectionFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.social.SocialAutoConfigurerAdapter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.social.connect.ConnectionFactory;
|
||||
import org.springframework.web.servlet.View;
|
||||
|
||||
|
||||
/**
|
||||
* 微信登录配置
|
||||
* @author lsy
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "ms.security.social.weChat", name = "app-id")
|
||||
public class WeChatAutoConfiguration extends SocialAutoConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory<?> createConnectionFactory() {
|
||||
WeChatSocialProperties weChatConfig = securityProperties.getSocial().getWeChat();
|
||||
return new WeChatConnectionFactory(weChatConfig.getProviderId(), weChatConfig.getAppId(),
|
||||
weChatConfig.getAppSecret());
|
||||
}
|
||||
/**
|
||||
* 定义绑定成功返回的视图,和返回失败时候的视图
|
||||
*/
|
||||
@Bean({"connect/weChatConnected","connect/weChatConnect"})
|
||||
@ConditionalOnMissingBean(name = "weChatConnectedView")
|
||||
public View weChatConnectedView(){
|
||||
MsConnectedView msConnectedView = new MsConnectedView();
|
||||
return msConnectedView;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.social.wechat.config;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.core.social.wechat.connet;
|
||||
|
||||
import org.springframework.social.oauth2.AccessGrant;
|
||||
|
||||
/**
|
||||
* 微信的access_token信息。与标准OAuth2协议不同,微信在获取access_token时会同时返回openId,并没有单独的通过accessToke换取openId的服务
|
||||
* 所以在这里继承了标准AccessGrant,添加了openId字段,作为对微信access_token信息的封装。
|
||||
* @author lsy
|
||||
*/
|
||||
public class WeChatAccessGrant extends AccessGrant {
|
||||
|
||||
|
||||
private String openId;
|
||||
|
||||
public WeChatAccessGrant() {
|
||||
super("");
|
||||
}
|
||||
|
||||
public WeChatAccessGrant(String accessToken, String scope, String refreshToken, Long expiresIn) {
|
||||
super(accessToken, scope, refreshToken, expiresIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the openId
|
||||
*/
|
||||
public String getOpenId() {
|
||||
return openId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param openId the openId to set
|
||||
*/
|
||||
public void setOpenId(String openId) {
|
||||
this.openId = openId;
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.core.social.wechat.connet;
|
||||
|
||||
import com.micro.fast.security.core.social.wechat.api.WeChat;
|
||||
import com.micro.fast.security.core.social.wechat.api.WeChatUserInfo;
|
||||
import org.springframework.social.connect.ApiAdapter;
|
||||
import org.springframework.social.connect.ConnectionValues;
|
||||
import org.springframework.social.connect.UserProfile;
|
||||
|
||||
/**
|
||||
* 微信 api适配器,将微信 api的数据模型转为spring social的标准模型。
|
||||
* @author lsy
|
||||
*/
|
||||
public class WeChatAdapter implements ApiAdapter<WeChat> {
|
||||
|
||||
private String openId;
|
||||
|
||||
public WeChatAdapter() {}
|
||||
|
||||
public WeChatAdapter(String openId){
|
||||
this.openId = openId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param api
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean test(WeChat api) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param api
|
||||
* @param values
|
||||
*/
|
||||
@Override
|
||||
public void setConnectionValues(WeChat api, ConnectionValues values) {
|
||||
WeChatUserInfo profile = api.getWeChatUserInfo(openId);
|
||||
values.setProviderUserId(profile.getOpenid());
|
||||
values.setDisplayName(profile.getNickname());
|
||||
values.setImageUrl(profile.getHeadimgurl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param api
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserProfile fetchUserProfile(WeChat api) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param api
|
||||
* @param message
|
||||
*/
|
||||
@Override
|
||||
public void updateStatus(WeChat api, String message) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.core.social.wechat.connet;
|
||||
|
||||
import com.micro.fast.security.core.social.wechat.api.WeChat;
|
||||
import org.springframework.social.connect.ApiAdapter;
|
||||
import org.springframework.social.connect.Connection;
|
||||
import org.springframework.social.connect.ConnectionData;
|
||||
import org.springframework.social.connect.support.OAuth2Connection;
|
||||
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
|
||||
import org.springframework.social.oauth2.AccessGrant;
|
||||
import org.springframework.social.oauth2.OAuth2ServiceProvider;
|
||||
|
||||
/**
|
||||
* 微信连接工厂
|
||||
* @author lsy
|
||||
*/
|
||||
public class WeChatConnectionFactory extends OAuth2ConnectionFactory<WeChat> {
|
||||
|
||||
/**
|
||||
* @param appId
|
||||
* @param appSecret
|
||||
*/
|
||||
public WeChatConnectionFactory(String providerId, String appId, String appSecret) {
|
||||
super(providerId, new WeChatServiceProvider(appId, appSecret), new WeChatAdapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* 由于微信的openId是和accessToken一起返回的,所以在这里直接根据accessToken设置providerUserId即可,不用像QQ那样通过QQAdapter来获取
|
||||
*/
|
||||
@Override
|
||||
protected String extractProviderUserId(AccessGrant accessGrant) {
|
||||
//如果是微信登录就直接返回 openId
|
||||
if(accessGrant instanceof WeChatAccessGrant) {
|
||||
return ((WeChatAccessGrant)accessGrant).getOpenId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param accessGrant
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Connection<WeChat> createConnection(AccessGrant accessGrant) {
|
||||
return new OAuth2Connection<WeChat>(getProviderId(), extractProviderUserId(accessGrant), accessGrant.getAccessToken(),
|
||||
accessGrant.getRefreshToken(), accessGrant.getExpireTime(), getOAuth2ServiceProvider(), getApiAdapter(extractProviderUserId(accessGrant)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Connection<WeChat> createConnection(ConnectionData data) {
|
||||
return new OAuth2Connection<WeChat>(data, getOAuth2ServiceProvider(), getApiAdapter(data.getProviderUserId()));
|
||||
}
|
||||
|
||||
private ApiAdapter<WeChat> getApiAdapter(String providerUserId) {
|
||||
return new WeChatAdapter(providerUserId);
|
||||
}
|
||||
|
||||
private OAuth2ServiceProvider<WeChat> getOAuth2ServiceProvider() {
|
||||
return (OAuth2ServiceProvider<WeChat>) getServiceProvider();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.core.social.wechat.connet;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.social.oauth2.AccessGrant;
|
||||
import org.springframework.social.oauth2.OAuth2Parameters;
|
||||
import org.springframework.social.oauth2.OAuth2Template;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* 完成微信的OAuth2认证流程的模板类。国内厂商实现的OAuth2每个都不同, spring默认提供的OAuth2Template适应不了,只能针对每个厂商自己微调。
|
||||
* @author lsy
|
||||
*/
|
||||
public class WeChatOAuth2Template extends OAuth2Template {
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private String accessTokenUrl;
|
||||
|
||||
private static final String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token";
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
public WeChatOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
|
||||
super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
|
||||
setUseParametersForClientAuthentication(true);
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.accessTokenUrl = accessTokenUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.social.oauth2.OAuth2Template#exchangeForAccess(java.lang.String, java.lang.String, org.springframework.util.MultiValueMap)
|
||||
*/
|
||||
@Override
|
||||
public AccessGrant exchangeForAccess(String authorizationCode, String redirectUri,
|
||||
MultiValueMap<String, String> parameters) {
|
||||
StringBuilder accessTokenRequestUrl = new StringBuilder(accessTokenUrl);
|
||||
accessTokenRequestUrl.append("?appid="+clientId);
|
||||
accessTokenRequestUrl.append("&secret="+clientSecret);
|
||||
accessTokenRequestUrl.append("&code="+authorizationCode);
|
||||
accessTokenRequestUrl.append("&grant_type=authorization_code");
|
||||
accessTokenRequestUrl.append("&redirect_uri="+redirectUri);
|
||||
return getAccessToken(accessTokenRequestUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessGrant refreshAccess(String refreshToken, MultiValueMap<String, String> additionalParameters) {
|
||||
|
||||
StringBuilder refreshTokenUrl = new StringBuilder(REFRESH_TOKEN_URL);
|
||||
|
||||
refreshTokenUrl.append("?appid="+clientId);
|
||||
refreshTokenUrl.append("&grant_type=refresh_token");
|
||||
refreshTokenUrl.append("&refresh_token="+refreshToken);
|
||||
|
||||
return getAccessToken(refreshTokenUrl);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private AccessGrant getAccessToken(StringBuilder accessTokenRequestUrl) {
|
||||
|
||||
logger.info("获取access_token, 请求URL: "+accessTokenRequestUrl.toString());
|
||||
|
||||
String response = getRestTemplate().getForObject(accessTokenRequestUrl.toString(), String.class);
|
||||
|
||||
logger.info("获取access_token, 响应内容: "+response);
|
||||
|
||||
Map<String, Object> result = null;
|
||||
try {
|
||||
result = new ObjectMapper().readValue(response, Map.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
//返回错误码时直接返回空
|
||||
if(StringUtils.isNotBlank(MapUtils.getString(result, "errcode"))){
|
||||
String errcode = MapUtils.getString(result, "errcode");
|
||||
String errmsg = MapUtils.getString(result, "errmsg");
|
||||
throw new RuntimeException("获取access token失败, errcode:"+errcode+", errmsg:"+errmsg);
|
||||
}
|
||||
|
||||
WeChatAccessGrant accessToken = new WeChatAccessGrant(
|
||||
MapUtils.getString(result, "access_token"),
|
||||
MapUtils.getString(result, "scope"),
|
||||
MapUtils.getString(result, "refresh_token"),
|
||||
MapUtils.getLong(result, "expires_in"));
|
||||
|
||||
accessToken.setOpenId(MapUtils.getString(result, "openid"));
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建获取授权码的请求。也就是引导用户跳转到微信的地址。
|
||||
*/
|
||||
@Override
|
||||
public String buildAuthenticateUrl(OAuth2Parameters parameters) {
|
||||
String url = super.buildAuthenticateUrl(parameters);
|
||||
url = url + "&appid="+clientId+"&scope=snsapi_login";
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildAuthorizeUrl(OAuth2Parameters parameters) {
|
||||
return buildAuthenticateUrl(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信返回的contentType是html/text,添加相应的HttpMessageConverter来处理。
|
||||
*/
|
||||
@Override
|
||||
protected RestTemplate createRestTemplate() {
|
||||
RestTemplate restTemplate = super.createRestTemplate();
|
||||
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.micro.fast.security.core.social.wechat.connet;
|
||||
|
||||
import com.micro.fast.security.core.social.wechat.api.WeChat;
|
||||
import com.micro.fast.security.core.social.wechat.api.impl.WeChatImpl;
|
||||
import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
* 微信的OAuth2流程处理器的提供器,供spring social的connect体系调用
|
||||
*
|
||||
* @author zhailiang
|
||||
*
|
||||
*/
|
||||
public class WeChatServiceProvider extends AbstractOAuth2ServiceProvider<WeChat> {
|
||||
|
||||
/**
|
||||
* 微信获取授权码的url
|
||||
*/
|
||||
private static final String URL_AUTHORIZE = "https://open.weixin.qq.com/connect/qrconnect";
|
||||
/**
|
||||
* 微信获取accessToken的url
|
||||
*/
|
||||
private static final String URL_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token";
|
||||
|
||||
/**
|
||||
* @param appId
|
||||
* @param appSecret
|
||||
*/
|
||||
public WeChatServiceProvider(String appId, String appSecret) {
|
||||
super(new WeChatOAuth2Template(appId, appSecret,URL_AUTHORIZE,URL_ACCESS_TOKEN));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public WeChat getApi(String accessToken) {
|
||||
return new WeChatImpl(accessToken);
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.social.wechat.connet;
|
||||
+1
@@ -0,0 +1 @@
|
||||
package com.micro.fast.security.core.social.wechat;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.micro.fast.security.core.validate.code;
|
||||
|
||||
import com.micro.fast.security.core.validate.code.pojo.ValidateCode;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* 验证码的存储
|
||||
* @author lsy
|
||||
*/
|
||||
public interface ValidateCodeRepository {
|
||||
|
||||
String SMS_CODE = "sms";
|
||||
String IMAGE_CODE = "image";
|
||||
|
||||
/**
|
||||
* 保存验证码
|
||||
* @param request
|
||||
* @param code
|
||||
* @param validateCodeType
|
||||
*/
|
||||
void save(ServletWebRequest request, ValidateCode code,String validateCodeType);
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
* @param request
|
||||
* @param validateCodeType
|
||||
* @return
|
||||
*/
|
||||
ValidateCode get(ServletWebRequest request,String validateCodeType);
|
||||
|
||||
/**
|
||||
* 删除验证码
|
||||
* @param request
|
||||
* @param validateCodeType
|
||||
*/
|
||||
void remove(ServletWebRequest request,String validateCodeType);
|
||||
}
|
||||
+22
-4
@@ -2,14 +2,17 @@ package com.micro.fast.security.core.validate.code.filter;
|
||||
|
||||
import com.micro.fast.security.core.master.SecurityConstants;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.core.validate.code.ValidateCodeRepository;
|
||||
import com.micro.fast.security.core.validate.code.exception.ValidateCodeException;
|
||||
import com.micro.fast.security.core.validate.code.pojo.ValidateCode;
|
||||
import com.micro.fast.security.core.validate.code.processor.ValidateCodeProcessor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
|
||||
import org.springframework.social.connect.web.SessionStrategy;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
@@ -18,6 +21,7 @@ import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
@@ -31,8 +35,13 @@ public class ValidateCodeFilter extends OncePerRequestFilter implements Initiali
|
||||
public static final String IMAGE = "IMAGE";
|
||||
public static final String SMS = "SMS";
|
||||
|
||||
|
||||
private AuthenticationFailureHandler authenticationFailureHandler;
|
||||
|
||||
private ValidateCodeRepository validateCodeRepository;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* session操作工具类
|
||||
*/
|
||||
@@ -86,7 +95,6 @@ public class ValidateCodeFilter extends OncePerRequestFilter implements Initiali
|
||||
, HttpServletResponse httpServletResponse
|
||||
, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
//判断是否需要验证码校验
|
||||
boolean filter = false;
|
||||
//进行校验的类型
|
||||
@@ -125,7 +133,10 @@ public class ValidateCodeFilter extends OncePerRequestFilter implements Initiali
|
||||
* @param validateType
|
||||
*/
|
||||
private void validate(ServletWebRequest servletWebRequest, String validateType) {
|
||||
ValidateCode validateCode = (ValidateCode) (sessionStrategy.getAttribute(servletWebRequest, ValidateCodeProcessor.SESSION_KEY_PREFIX + validateType));
|
||||
//
|
||||
//获取存储的验证码
|
||||
ValidateCode validateCode = validateCodeRepository.get(servletWebRequest, validateType.toLowerCase());
|
||||
|
||||
String code = servletWebRequest.getParameter(validateType.toLowerCase()+"Code");
|
||||
|
||||
//session和用户传入的验证码不能为空
|
||||
@@ -139,8 +150,8 @@ public class ValidateCodeFilter extends OncePerRequestFilter implements Initiali
|
||||
if (validateCode.isExpire()) {
|
||||
throw new ValidateCodeException("验证码已过期");
|
||||
}
|
||||
//一次校验完成之后删除验证码
|
||||
sessionStrategy.removeAttribute(servletWebRequest, ValidateCodeProcessor.SESSION_KEY_PREFIX + validateType);
|
||||
//如果校验通过之后删除验证码
|
||||
validateCodeRepository.remove(servletWebRequest,validateType.toLowerCase());
|
||||
}
|
||||
|
||||
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
|
||||
@@ -166,4 +177,11 @@ public class ValidateCodeFilter extends OncePerRequestFilter implements Initiali
|
||||
public void setSecurityProperties(SecurityProperties securityProperties) {
|
||||
this.securityProperties = securityProperties;
|
||||
}
|
||||
public ValidateCodeRepository getValidateCodeRepository() {
|
||||
return validateCodeRepository;
|
||||
}
|
||||
|
||||
public void setValidateCodeRepository(ValidateCodeRepository validateCodeRepository) {
|
||||
this.validateCodeRepository = validateCodeRepository;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-8
@@ -1,5 +1,6 @@
|
||||
package com.micro.fast.security.core.validate.code.processor.impl;
|
||||
|
||||
import com.micro.fast.security.core.validate.code.ValidateCodeRepository;
|
||||
import com.micro.fast.security.core.validate.code.pojo.ValidateCode;
|
||||
import com.micro.fast.security.core.validate.code.processor.ValidateCodeProcessor;
|
||||
import com.micro.fast.security.core.validate.code.util.ValidateCodeUtil;
|
||||
@@ -20,10 +21,7 @@ import java.util.Map;
|
||||
public abstract class AbstractValidateCodeProcessor <T extends ValidateCode> implements ValidateCodeProcessor {
|
||||
|
||||
|
||||
/**
|
||||
* 操作session的工具类
|
||||
*/
|
||||
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
|
||||
|
||||
|
||||
/**
|
||||
* 收集系统中所有{@link ValidateCodeUtil}的实现,并将bean的名字作为key
|
||||
@@ -33,6 +31,9 @@ public abstract class AbstractValidateCodeProcessor <T extends ValidateCode> imp
|
||||
|
||||
private static final String VALIDATE_CODE_UTIL_SUFFIX = "CodeUtil";
|
||||
|
||||
@Autowired
|
||||
private ValidateCodeRepository validateCodeRepository;
|
||||
|
||||
@Override
|
||||
public void create(ServletWebRequest servletWebRequest) throws ServletRequestBindingException, IOException {
|
||||
T generateCode = generateCode(servletWebRequest);
|
||||
@@ -63,10 +64,7 @@ public abstract class AbstractValidateCodeProcessor <T extends ValidateCode> imp
|
||||
ValidateCode saveValidateCode = new ValidateCode();
|
||||
saveValidateCode.setCode(validateCode.getCode());
|
||||
saveValidateCode.setExpireTime(validateCode.getExpireTime());
|
||||
//向session中存储验证码
|
||||
sessionStrategy.setAttribute(servletWebRequest
|
||||
,SESSION_KEY_PREFIX+getProcessorType(servletWebRequest).toUpperCase()
|
||||
,saveValidateCode);
|
||||
validateCodeRepository.save(servletWebRequest,saveValidateCode,getProcessorType(servletWebRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.micro.fast</groupId>
|
||||
<artifactId>boot-starter-security-browser</artifactId>
|
||||
<artifactId>boot-starter-security-app</artifactId>
|
||||
<version>${micro.fast.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -44,7 +44,13 @@
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!--jwt令牌解析-->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.7.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
-1
@@ -11,7 +11,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
@EnableSwagger2
|
||||
public class BootStarterSecurityDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.micro.fast.security.demo;
|
||||
|
||||
import org.springframework.social.connect.Connection;
|
||||
import org.springframework.social.connect.ConnectionSignUp;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DemoConnectionSignUp implements ConnectionSignUp {
|
||||
@Override
|
||||
public String execute(Connection<?> connection) {
|
||||
//根据社交用户信息,默认创建用户,并返回用户的唯一标识
|
||||
return connection.getDisplayName();
|
||||
}
|
||||
}
|
||||
+14
-2
@@ -1,12 +1,19 @@
|
||||
package com.micro.fast.security.demo.controller;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.micro.fast.security.core.master.SecurityProperties;
|
||||
import com.micro.fast.security.demo.pojo.User;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -14,11 +21,17 @@ import java.util.List;
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class RestfulController {
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@GetMapping("/{id:\\d+}")//使用正则表达式指定传入的参数只能是数字
|
||||
@JsonView(User.DetailView.class)
|
||||
public User getInfo(@PathVariable("id") String id){
|
||||
public User getInfo(@PathVariable("id") String id, HttpServletRequest request) throws UnsupportedEncodingException {
|
||||
User user = new User("tom");
|
||||
String authorization = request.getHeader("Authorization");
|
||||
String token = StringUtils.substringAfter(authorization, "bearer ");
|
||||
//解析jwt
|
||||
Claims body = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("UTF-8")).parseClaimsJws(token).getBody();
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -49,7 +62,6 @@ public class RestfulController {
|
||||
System.out.println(s);
|
||||
});
|
||||
}
|
||||
|
||||
System.out.println(user.getName());
|
||||
System.out.println(user.getPassword());
|
||||
System.out.println(user.getBirthday());
|
||||
|
||||
+16
-5
@@ -1,14 +1,14 @@
|
||||
spring:
|
||||
session:
|
||||
store-type: none # 暂时不开启session存储
|
||||
# session:
|
||||
# store-type: redis
|
||||
datasource:
|
||||
url: jdbc:mysql://127.0.0.1:3317/micro?useUncoide=true&chracterEncoding=utf-8&useSSL=false
|
||||
username: root
|
||||
password: studyj2e
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
redis:
|
||||
host: fthipw.natappfree.cc
|
||||
port: 6379
|
||||
# redis:
|
||||
# host: fthipw.natappfree.cc
|
||||
# port: 6379
|
||||
#security:
|
||||
# basic:
|
||||
# enabled: true
|
||||
@@ -20,4 +20,15 @@ ms:
|
||||
code:
|
||||
image:
|
||||
length: 4
|
||||
# 应用session的管理
|
||||
server:
|
||||
session:
|
||||
# 超时时间,单位是秒,但是不能小于一分钟
|
||||
timeout: 600
|
||||
# token的配置
|
||||
security:
|
||||
oauth2:
|
||||
client:
|
||||
client-id: 授权给谁
|
||||
client-secret: 授权给谁的密码
|
||||
|
||||
|
||||
@@ -5,25 +5,11 @@
|
||||
目中引入该依赖进行相关配置即可使用.
|
||||
### boot-starter-ssm项目的参数配置
|
||||
得益于springboot的自动配置以及条件化装配的机制.我们可以扩展封装自己的ssm-boot,也是就是本项目中的commom,本项目的自定义扩展配置如下:
|
||||
- `jdbc.master.username` 用户名(必要参数)
|
||||
- `jdbc.master.password` 密码是使用common模块中的AESUtil加密过后的(必要参数)
|
||||
- `jdbc.master.url` 数据库连接的url(必要参数)
|
||||
- `jdbc.master.driverClass` 数据库连接驱动类(必要参数)
|
||||
- `druid.master.servletInitParams` druid数据连接池的servlet的初始化参数,默认的druid账号密码是micro,fast.如果需要自定义可以传入自己的配
|
||||
置,使用key:value的数组的形式.
|
||||
- `druid.master.servletUrl` druid数据库连接池的访问路径,默认访问路径是/druid/*
|
||||
- `druid.master.beanNames` druid要监控的bean的名字,多个使用逗号分割
|
||||
- `druid.master.defaultAutoCommit` 事务是否自动提交,默认为true
|
||||
- `druid.master.initialSize` 连接池的初始化大小,默认是20
|
||||
- `druid.master.maxActive` 连接池的最大的数量,默认是50
|
||||
- `druid.master.minIdle`连接池的最小空闲数量,默认是10
|
||||
- `druid.master.maxWait` 当连接池中连接已经用完了,等待建立一个新连接的最大毫秒数,默认是1000
|
||||
- `druid.master.minEvictableIdleTimeMillis` 等待到达多长时间释放连接池,默认是3600000
|
||||
- 参考[druid官网的配置说明](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter)
|
||||
- `mybatis.typeAliasesPackage` pojo包所在的位置(必要参数),如果要指定多个包的话使用逗号分割
|
||||
- `mybatis.mappers` mapper映射文件所在的路径(必要参数)
|
||||
- `mybatis.pageHelperProperties` pagehelper分页插件的properties配置形式是key:value数组的形式,默认方言是mysql,如果需要自定义可以传入自己的配置,使用key:value的数
|
||||
组的形式.
|
||||
- 除此之外`mybatis`的`dao`层扫描的配置需要在项目中自行配置
|
||||
- 除此之外`common`实现了主从两个数据源的配置,从数据库的配置只需将`master`替换为`slave`.主从数据源默认使用相同的`druid`监控视图.其中对于`beanbeanNames`的监控配置,`servletUrl`的配
|
||||
置,`servletInitParams`的配置在`master`中进行配置,需要在使用的项目之中去除默认的数据源自动装配`@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)`
|
||||
- 需要注意的是:当使用多数据源的时候,需要在使用的项目之中去除默认的数据源自动装配`@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)`
|
||||
- 对于数据源的切换方法是使用aop的方式对`@SwitchDataSource('数据源的名字')`进行拦截动态切换数据源,主数据源的名字是`master`,从数据源的名字是`slave`.对于没有注解默认的数据源是`master`.
|
||||
+1
-1
@@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @since 1.0
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = {"com.micro.fast.common.config"})
|
||||
@ComponentScan(basePackages = {"com.micro.fast.common"})
|
||||
public class CommonApplication {
|
||||
// public static void main(String[] args) {
|
||||
// SpringApplication.run(CommonApplication.class, args);
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@ public class DynamicDataSourceConfig {
|
||||
/**
|
||||
* 从数据源
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
@Autowired
|
||||
@Qualifier(MASTER_DATA_SOURCE)
|
||||
private DataSource masterDataSource;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class DynamicDataSourceConfig {
|
||||
dynamicDataSource.setDefaultTargetDataSource(dataSource);
|
||||
dataSourceMap.put(MASTER,dataSource);
|
||||
this.dataSourceMap.forEach((key,value) -> {
|
||||
if ( DYNAMIC_DATA_SOURCE.equals(key)
|
||||
if ( !DYNAMIC_DATA_SOURCE.equals(key)
|
||||
&&!DATA_SOURCE.equals(key)
|
||||
&& !MASTER.equals(key)){
|
||||
dataSourceMap.put(key,value);
|
||||
@@ -76,7 +76,7 @@ public class DynamicDataSourceConfig {
|
||||
dataSourceMap.put(MASTER,masterDataSource);
|
||||
dataSourceMap.put(SLAVE,dataSource);
|
||||
this.dataSourceMap.forEach((key,value) -> {
|
||||
if (DYNAMIC_DATA_SOURCE.equals(key)
|
||||
if (!DYNAMIC_DATA_SOURCE.equals(key)
|
||||
&&!DATA_SOURCE.equals(key)
|
||||
&&!MASTER_DATA_SOURCE.equals(key)
|
||||
&& !MASTER.equals(key)
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.micro.fast.common.exception;
|
||||
|
||||
|
||||
/**
|
||||
* 系统级别的异常,比如数据哭插入失败
|
||||
* @author lsy
|
||||
*/
|
||||
public class SystemException extends RuntimeException {
|
||||
public SystemException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user