新增 spring social登录

This commit is contained in:
wangiegie@gmail.com 2018-01-18 23:24:53 +08:00
parent 297af877bd
commit 2112f3e0b9
31 changed files with 972 additions and 35 deletions

View File

@ -14,6 +14,7 @@ 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.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@ -27,7 +28,7 @@ import java.io.PrintWriter;
* 手机号登录成功返回oauth token
*/
@Component
public class MobileLoginSuccessHandler implements org.springframework.security.web.authentication.AuthenticationSuccessHandler {
public class MobileLoginSuccessHandler implements AuthenticationSuccessHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectMapper objectMapper;

View File

@ -6,6 +6,7 @@ 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.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
@ -17,7 +18,7 @@ import org.springframework.stereotype.Component;
@Component
public class MobileSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private MobileLoginSuccessHandler mobileLoginSuccessHandler;
private AuthenticationSuccessHandler mobileLoginSuccessHandler;
@Autowired
private UserService userService;

View File

@ -0,0 +1,31 @@
package com.github.pig.auth.component.social;
import com.github.pig.common.constant.SecurityConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.social.security.SocialAuthenticationFilter;
import org.springframework.social.security.SpringSocialConfigurer;
import org.springframework.stereotype.Component;
/**
* 继承默认的社交登录配置加入自定义的后处理逻辑
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Component
public class PigSocialConfigurer extends SpringSocialConfigurer {
@Autowired
private AuthenticationSuccessHandler socialLoginSuccessHandler;
@Override
protected <T> T postProcess(T object) {
SocialAuthenticationFilter filter = (SocialAuthenticationFilter) super.postProcess(object);
filter.setFilterProcessesUrl(SecurityConstants.DEFAULT_SOCIAL_PROCESS_URL);
filter.setAlwaysUsePostLoginUrl(true);
filter.setAuthenticationSuccessHandler(socialLoginSuccessHandler);
return (T) filter;
}
}

View File

@ -0,0 +1,34 @@
package com.github.pig.auth.component.social;
import com.github.pig.common.constant.SecurityConstants;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.web.ProviderSignInUtils;
import org.springframework.social.security.SpringSocialConfigurer;
/**
* 社交登录配置主类
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
/**
* 处理注册流程的工具类
*
* @param factoryLocator
* @return
*/
@Bean
public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator factoryLocator) {
return new ProviderSignInUtils(factoryLocator, getUsersConnectionRepository(factoryLocator));
}
}

View File

@ -0,0 +1,27 @@
package com.github.pig.auth.component.social;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* Created on 2018/1/11 0011.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
public class SocialConnectView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("text/html;charset=UTF-8");
if (model.get("connection") == null) {
response.getWriter().write("<h3>解绑成功</h3>");
} else {
response.getWriter().write("<h3>绑定成功</h3>");
}
}
}

View File

@ -0,0 +1,45 @@
package com.github.pig.auth.component.social;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.UsersConnectionRepository;
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;
import java.util.Set;
/**
* 访问/connect会获取所有绑定社交的信息(显示是否绑定第三方社交信息)
* Created on 2018/1/11 0011.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Component("connect/status")
public class SocialConnectionStatusView extends AbstractView {
@Autowired
private ObjectMapper objectMapper;
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, List<Connection<?>>> connections = (Map<String, List<Connection<?>>>) model.get("connectionMap");
Map<String, Boolean> result = new HashMap<>();
for (String key : connections.keySet()) {
result.put(key, CollectionUtils.isNotEmpty(connections.get(key)));
}
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(result));
}
}

View File

@ -0,0 +1,80 @@
package com.github.pig.auth.component.social;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pig.auth.config.AuthServerConfig;
import com.github.pig.auth.config.PigAuthorizationConfig;
import com.github.pig.common.constant.CommonConstant;
import com.xiaoleilu.hutool.util.MapUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author lengleng
* @date 2018年01月18日09:44:16
* 社交登录成功返回oauth token
*/
@Component
public class SocialLoginSuccessHandler implements AuthenticationSuccessHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectMapper objectMapper;
@Autowired
private AuthServerConfig AuthServerConfig;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationServerTokenServices authorizationServerTokenServices;
/**
* Called when a user has been successfully authenticated.
* 调用spring security oauth API 生成 oAuth2AccessToken
*
* @param request the request which caused the successful authentication
* @param response the response
* @param authentication the <tt>Authentication</tt> object which was created during
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
try {
String clientId = AuthServerConfig.getClientId();
String clientSecret = AuthServerConfig.getClientSecret();
JSONObject params = new JSONObject();
params.put("clientId", clientId);
params.put("clientSecret", clientSecret);
params.put("authentication", authentication);
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
TokenRequest tokenRequest = new TokenRequest(MapUtil.newHashMap(), clientId, clientDetails.getScope(), "social");
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
logger.info("获取token 成功:{}", oAuth2AccessToken.getValue());
response.setCharacterEncoding(CommonConstant.UTF8);
response.setContentType(CommonConstant.CONTENT_TYPE);
PrintWriter printWriter = response.getWriter();
printWriter.append(objectMapper.writeValueAsString(oAuth2AccessToken));
} catch (IOException e) {
throw new BadCredentialsException(
"Failed to decode basic authentication token");
}
}
}

View File

@ -0,0 +1,23 @@
package com.github.pig.auth.component.social;
import lombok.Data;
/**
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Data
public class SocialUserInfo {
private String providerId;
private String providerUserId;
private String nickname;
private String headImg;
}

View File

@ -0,0 +1,16 @@
package com.github.pig.auth.component.social.qq.api;
/**
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
public interface QQ {
/**
* 获取用户信息
* @return
*/
QQUserInfo getUserInfo();
}

View File

@ -0,0 +1,91 @@
package com.github.pig.auth.component.social.qq.api;
import lombok.Data;
/**
* qq互联http://wiki.connect.qq.com/get_user_info
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Data
public class QQUserInfo {
/**
* 返回码
*/
private String ret;
/**
* 如果ret<0会有相应的错误信息提示返回数据全部用UTF-8编码
*/
private String msg;
/**
*
*/
private String openId;
/**
* 不知道什么东西文档上没写但是实际api返回里有
*/
private String is_lost;
/**
* (直辖市)
*/
private String province;
/**
* (直辖市区)
*/
private String city;
/**
* 出生年月
*/
private String year;
/**
* 用户在QQ空间的昵称
*/
private String nickname;
/**
* 大小为30×30像素的QQ空间头像URL
*/
private String figureurl;
/**
* 大小为50×50像素的QQ空间头像URL
*/
private String figureurl_1;
/**
* 大小为100×100像素的QQ空间头像URL
*/
private String figureurl_2;
/**
* 大小为40×40像素的QQ头像URL
*/
private String figureurl_qq_1;
/**
* 大小为100×100像素的QQ头像URL需要注意不是所有的用户都拥有QQ的100×100的头像但40×40像素则是一定会有
*/
private String figureurl_qq_2;
/**
* 性别 如果获取不到则默认返回
*/
private String gender;
/**
* 标识用户是否为黄钻用户0不是1
*/
private String is_yellow_vip;
/**
* 标识用户是否为黄钻用户0不是1
*/
private String vip;
/**
* 黄钻等级
*/
private String yellow_vip_level;
/**
* 黄钻等级
*/
private String level;
/**
* 标识是否为年费黄钻用户0不是 1
*/
private String is_yellow_year_vip;
}

View File

@ -0,0 +1,69 @@
package com.github.pig.auth.component.social.qq.api.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pig.auth.component.social.qq.api.QQ;
import com.github.pig.auth.component.social.qq.api.QQUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.social.oauth2.TokenStrategy;
/**
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Slf4j
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
private static final String QQ_URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
private static final String QQ_URL_GET_USER_INFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";
/**
* appId 配置文件读取
*/
private String appId;
/**
* openId 请求QQ_URL_GET_OPENID返回
*/
private String openId;
/**
* 工具类
*/
private ObjectMapper objectMapper = new ObjectMapper();
/**
* 构造方法获取openId
*/
public QQImpl(String accessToken, String appId) {
//access_token作为查询参数来携带
super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
this.appId = appId;
String url = String.format(QQ_URL_GET_OPENID, accessToken);
String result = getRestTemplate().getForObject(url, String.class);
log.info("【QQImpl】 QQ_URL_GET_OPENID={} result={}", QQ_URL_GET_OPENID, result);
this.openId = StringUtils.substringBetween(result, "\"openid\":\"", "\"}");
}
@Override
public QQUserInfo getUserInfo() {
String url = String.format(QQ_URL_GET_USER_INFO, appId, openId);
String result = getRestTemplate().getForObject(url, String.class);
log.info("【QQImpl】 QQ_URL_GET_USER_INFO={} result={}", QQ_URL_GET_USER_INFO, result);
QQUserInfo userInfo = null;
try {
userInfo = objectMapper.readValue(result, QQUserInfo.class);
userInfo.setOpenId(openId);
return userInfo;
} catch (Exception e) {
throw new RuntimeException("获取用户信息失败", e);
}
}
}

View File

@ -0,0 +1,40 @@
package com.github.pig.auth.component.social.qq.config;
import com.github.pig.auth.component.social.qq.connect.QQConnectionFactory;
import com.github.pig.auth.component.social.repository.PigUsersConnectionRepository;
import com.github.pig.common.constant.SecurityConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.social.SocialAutoConfigurerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.social.connect.ConnectionFactory;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.social.connect.UsersConnectionRepository;
/**
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Configuration
public class QQAuthConfig extends SocialAutoConfigurerAdapter {
@Autowired
private ConnectionSignUp myConnectionSignUp;
@Override
protected ConnectionFactory<?> createConnectionFactory() {
return new QQConnectionFactory(SecurityConstants.DEFAULT_SOCIAL_QQ_PROVIDER_ID, SecurityConstants.DEFAULT_SOCIAL_QQ_APP_ID, SecurityConstants.DEFAULT_SOCIAL_QQ_APP_SECRET);
}
@Bean(name = "usersConnectionRepository")
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
PigUsersConnectionRepository repository = new PigUsersConnectionRepository();
repository.setConnectionSignUp(myConnectionSignUp);
return repository;
}
}

View File

@ -0,0 +1,21 @@
package com.github.pig.auth.component.social.qq.connect;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.stereotype.Component;
/**
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Component
public class MyConnectionSignUp implements ConnectionSignUp {
@Override
public String execute(Connection<?> connection) {
//根据社交用户信息默认创建用户并返回用户唯一标识
return connection.getKey().getProviderUserId();
}
}

View File

@ -0,0 +1,42 @@
package com.github.pig.auth.component.social.qq.connect;
import com.github.pig.auth.component.social.qq.api.QQ;
import com.github.pig.auth.component.social.qq.api.QQUserInfo;
import org.springframework.social.connect.ApiAdapter;
import org.springframework.social.connect.ConnectionValues;
import org.springframework.social.connect.UserProfile;
/**
* qq返回的信息为spring social提供的适配器
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
public class QQAdapter implements ApiAdapter<QQ> {
@Override
public boolean test(QQ api) {
return true;
}
@Override
public void setConnectionValues(QQ api, ConnectionValues values) {
QQUserInfo userInfo = api.getUserInfo();
values.setProviderUserId(userInfo.getOpenId());
values.setDisplayName(userInfo.getNickname());
values.setImageUrl(userInfo.getFigureurl_qq_1());
values.setProfileUrl(null);
}
@Override
public UserProfile fetchUserProfile(QQ api) {
return null;
}
@Override
public void updateStatus(QQ api, String message) {
}
}

View File

@ -0,0 +1,18 @@
package com.github.pig.auth.component.social.qq.connect;
import com.github.pig.auth.component.social.qq.api.QQ;
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
/**
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {
public QQConnectionFactory(String providerId, String appId, String appSecret) {
super(providerId, new QQServiceProvider(appId, appSecret), new QQAdapter());
}
}

View File

@ -0,0 +1,55 @@
package com.github.pig.auth.component.social.qq.connect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
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;
/**
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
@Slf4j
public class QQOAuth2Template extends OAuth2Template {
public QQOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
setUseParametersForClientAuthentication(true);
}
@Override
protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
String responseStr = getRestTemplate().postForObject(accessTokenUrl, parameters, String.class);
log.info("【QQOAuth2Template】获取accessToke的响应responseStr={}" + responseStr);
String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(responseStr, "&");
//http://wiki.connect.qq.com/使用authorization_code获取access_token
//access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
String accessToken = StringUtils.substringAfterLast(items[0], "=");
Long expiresIn = new Long(StringUtils.substringAfterLast(items[1], "="));
String refreshToken = StringUtils.substringAfterLast(items[2], "=");
return new AccessGrant(accessToken, null, refreshToken, expiresIn);
}
/**
* 日志debug模式才打印出来 处理qq返回的text/html 类型数据
*
* @return
*/
@Override
protected RestTemplate createRestTemplate() {
RestTemplate restTemplate = super.createRestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
return restTemplate;
}
}

View File

@ -0,0 +1,37 @@
package com.github.pig.auth.component.social.qq.connect;
import com.github.pig.auth.component.social.qq.api.QQ;
import com.github.pig.auth.component.social.qq.api.impl.QQImpl;
import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider;
/**
* http://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token
* Created on 2018/1/8 0008.
*
* @author zlf
* @email i@merryyou.cn
* @since 1.0
*/
public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {
/**
* 获取code
*/
private static final String QQ_URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize";
/**
* 获取access_token 也就是令牌
*/
private static final String QQ_URL_ACCESS_TOKEN = "https://graph.qq.com/oauth2.0/token";
private String appId;
public QQServiceProvider(String appId, String appSecret) {
super(new QQOAuth2Template(appId, appSecret, QQ_URL_AUTHORIZE, QQ_URL_ACCESS_TOKEN));
this.appId = appId;
}
@Override
public QQ getApi(String accessToken) {
return new QQImpl(accessToken, appId);
}
}

View File

@ -0,0 +1,173 @@
package com.github.pig.auth.component.social.repository;
import org.springframework.social.connect.*;
import org.springframework.util.MultiValueMap;
import java.util.List;
/**
* @author lengleng
* @date 2018/1/18
*/
public class PigConnectionRepository implements ConnectionRepository {
/**
* Find all connections the current user has across all providers.
* The returned map contains an entry for each provider the user is connected to.
* The key for each entry is the providerId, and the value is the list of {@link Connection}s that exist between the user and that provider.
* For example, if the user is connected once to Facebook and twice to Twitter, the returned map would contain two entries with the following structure:
* <pre>
* {
* "facebook" -&gt; Connection("Keith Donald") ,
* "twitter" -&gt; Connection("kdonald"), Connection("springsource")
* }
* </pre>
* The returned map is sorted by providerId and entry values are ordered by rank.
* Returns an empty map if the user has no connections.
*
* @return all connections the current user has across all providers.
*/
@Override
public MultiValueMap<String, Connection<?>> findAllConnections() {
return null;
}
/**
* Find the connections the current user has to the provider registered by the given id e.g. 'facebook'.
* The returned list is ordered by connection rank.
* Returns an empty list if the user has no connections to the provider.
*
* @param providerId the provider id e.g. "facebook"
* @return the connections the user has to the provider, or an empty list if none
*/
@Override
public List<Connection<?>> findConnections(String providerId) {
return null;
}
/**
* Find the connections the current user has to the provider of the given API e.g. Facebook.class.
* Semantically equivalent to {@link #findConnections(String)}, but uses the apiType as the provider key instead of the providerId.
* Useful for direct use by application code to obtain parameterized Connection instances e.g. <code>List&lt;Connection&lt;Facebook&gt;&gt;</code>.
*
* @param apiType the API type e.g. Facebook.class or Twitter.class
* @return the connections the user has to the provider of the API, or an empty list if none
*/
@Override
public <A> List<Connection<A>> findConnections(Class<A> apiType) {
return null;
}
/**
* Find the connections the current user has to the given provider users.
* The providerUsers parameter accepts a map containing an entry for each provider the caller is interested in.
* The key for each entry is the providerId e.g. "facebook", and the value is a list of provider user ids to fetch connections to e.g. ("126500", "34521", "127243").
* The returned map has the same structure and order, except the provider userId values have been replaced by Connection instances.
* If no connection exists between the current user and a given provider user, a null value is returned for that position.
*
* @param providerUserIds the provider users map
* @return the provider user connection map
*/
@Override
public MultiValueMap<String, Connection<?>> findConnectionsToUsers(MultiValueMap<String, String> providerUserIds) {
return null;
}
/**
* Get a connection for the current user by its key, which consists of the providerId + providerUserId.
*
* @param connectionKey the service provider connection key
* @return the connection
* @throws NoSuchConnectionException if no such connection exists for the current user
*/
@Override
public Connection<?> getConnection(ConnectionKey connectionKey) {
return null;
}
/**
* Get a connection between the current user and the given provider user.
* Semantically equivalent to {@link #getConnection(ConnectionKey)}, but uses the apiType as the provider key instead of the providerId.
* Useful for direct use by application code to obtain a parameterized Connection instance.
*
* @param apiType the API type e.g. Facebook.class or Twitter.class
* @param providerUserId the provider user e.g. "126500".
* @return the connection
* @throws NoSuchConnectionException if no such connection exists for the current user
*/
@Override
public <A> Connection<A> getConnection(Class<A> apiType, String providerUserId) {
return null;
}
/**
* Get the "primary" connection the current user has to the provider of the given API e.g. Facebook.class.
* If the user has multiple connections to the provider associated with the given apiType, this method returns the one with the top rank (or priority).
* Useful for direct use by application code to obtain a parameterized Connection instance.
*
* @param apiType the API type e.g. Facebook.class or Twitter.class
* @return the primary connection
* @throws NotConnectedException if the user is not connected to the provider of the API
*/
@Override
public <A> Connection<A> getPrimaryConnection(Class<A> apiType) {
return null;
}
/**
* Find the "primary" connection the current user has to the provider of the given API e.g. Facebook.class.
* Semantically equivalent to {@link #getPrimaryConnection(Class)} but returns null if no connection is found instead of throwing an exception.
*
* @param apiType the API type e.g. Facebook.class or Twitter.class
* @return the primary connection, or <code>null</code> if not found
*/
@Override
public <A> Connection<A> findPrimaryConnection(Class<A> apiType) {
return null;
}
/**
* Add a new connection to this repository for the current user.
* After the connection is added, it can be retrieved later using one of the finders defined in this interface.
*
* @param connection the new connection to add to this repository
* @throws DuplicateConnectionException if the user already has this connection
*/
@Override
public void addConnection(Connection<?> connection) {
}
/**
* Update a Connection already added to this repository.
* Merges the field values of the given connection object with the values stored in the repository.
*
* @param connection the existing connection to update in this repository
*/
@Override
public void updateConnection(Connection<?> connection) {
}
/**
* Remove all Connections between the current user and the provider from this repository.
* Does nothing if no provider connections exist.
*
* @param providerId the provider id e.g. 'facebook'
*/
@Override
public void removeConnections(String providerId) {
}
/**
* Remove a single Connection for the current user from this repository.
* Does nothing if no such connection exists.
*
* @param connectionKey the connection key
*/
@Override
public void removeConnection(ConnectionKey connectionKey) {
}
}

View File

@ -0,0 +1,42 @@
package com.github.pig.auth.component.social.repository;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.social.connect.UsersConnectionRepository;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author lengleng
* @date 2018/1/18
* 覆盖原有的保存的逻辑不保存登录状态
*/
public class PigUsersConnectionRepository implements UsersConnectionRepository {
private ConnectionSignUp connectionSignUp;
public ConnectionSignUp getConnectionSignUp() {
return connectionSignUp;
}
public void setConnectionSignUp(ConnectionSignUp connectionSignUp) {
this.connectionSignUp = connectionSignUp;
}
@Override
public List<String> findUserIdsWithConnection(Connection<?> connection) {
return Collections.singletonList(connection.getKey().getProviderUserId());
}
@Override
public Set<String> findUserIdsConnectedTo(String providerId, Set<String> providerUserIds) {
return providerUserIds;
}
@Override
public ConnectionRepository createConnectionRepository(String userId) {
return new PigConnectionRepository();
}
}

View File

@ -1,6 +1,7 @@
package com.github.pig.auth.config;
import com.github.pig.auth.component.mobile.MobileSecurityConfigurer;
import com.github.pig.auth.component.social.PigSocialConfigurer;
import com.github.pig.common.bean.config.FilterUrlsPropertiesConifg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@ -21,6 +22,8 @@ public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
private FilterUrlsPropertiesConifg filterUrlsPropertiesConifg;
@Autowired
private MobileSecurityConfigurer mobileSecurityConfigurer;
@Autowired
private PigSocialConfigurer merryyouSpringSocialConfigurer;
@Override
public void configure(HttpSecurity http) throws Exception {
@ -32,7 +35,9 @@ public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
registry.anyRequest().authenticated()
.and()
.csrf().disable();
http.apply(mobileSecurityConfigurer);
http.apply(mobileSecurityConfigurer)
.and()
.apply(merryyouSpringSocialConfigurer);
}
}

View File

@ -5,6 +5,7 @@ import com.github.pig.common.util.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -23,8 +24,8 @@ public class UserController {
@RequestMapping("/user")
public Object user(Principal user) {
return user;
public Object user(Authentication authentication) {
return authentication.getPrincipal();
}
/**

View File

@ -29,4 +29,12 @@ public interface UserService {
*/
@GetMapping("/user/findUserByMobile/{mobile}")
UserVo findUserByMobile(@PathVariable("mobile") String mobile);
/**
* 根据OpenId查询用户信息
* @param openId openId
* @return UserVo
*/
@GetMapping("/user/findUserByOpenId/{openId}")
UserVo findUserByOpenId(@PathVariable("openId") String openId);
}

View File

@ -32,4 +32,16 @@ public class UserServiceFallbackImpl implements UserService {
logger.error("调用{}异常:{}", "通过手机号查询用户", mobile);
return null;
}
/**
* 根据OpenId查询用户信息
*
* @param openId openId
* @return UserVo
*/
@Override
public UserVo findUserByOpenId(String openId) {
logger.error("调用{}异常:{}", "通过OpenId查询用户", openId);
return null;
}
}

View File

@ -2,13 +2,22 @@ package com.github.pig.auth.serivce;
import com.github.pig.auth.feign.UserService;
import com.github.pig.auth.util.UserDetailsImpl;
import com.github.pig.common.vo.SysRole;
import com.github.pig.common.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.social.security.SocialUser;
import org.springframework.social.security.SocialUserDetails;
import org.springframework.social.security.SocialUserDetailsService;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author lengleng
@ -16,7 +25,7 @@ import java.io.Serializable;
* <p>
*/
@Service("userDetailService")
public class UserDetailServiceImpl implements UserDetailsService, Serializable {
public class UserDetailServiceImpl implements UserDetailsService,SocialUserDetailsService, Serializable {
@Autowired
private UserService userService;
@ -25,4 +34,19 @@ public class UserDetailServiceImpl implements UserDetailsService, Serializable {
UserVo userVo = userService.findUserByUsername(username);
return new UserDetailsImpl(userVo);
}
/**
* @param userId the user ID used to lookup the user details
* @return the SocialUserDetails requested
* @see UserDetailsService#loadUserByUsername(String)
*/
@Override
public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException {
UserVo userVo = userService.findUserByOpenId(userId);
List<GrantedAuthority> authorityList = new ArrayList<>();
for (SysRole role : userVo.getRoleList()) {
authorityList.add(new SimpleGrantedAuthority(role.getRoleCode()));
}
return new SocialUser(userVo.getUsername(),userVo.getPassword(), authorityList);
}
}

View File

@ -85,4 +85,23 @@ public interface SecurityConstants {
* 认证服务的SERVICEIDzuul 配置的对应
*/
String AUTH_SERVICE_ID = "auth-service";
/**
* qq appID
*/
String DEFAULT_SOCIAL_QQ_APP_ID = "101322838";
/**
* qq appsECRET
*/
String DEFAULT_SOCIAL_QQ_APP_SECRET = "fe6ec1ed3fc45e664ce8ddbf78376ab7";
/**
* 提供商的ID
*/
String DEFAULT_SOCIAL_QQ_PROVIDER_ID = "qq";
/**
* 默认的social的登录地址
*/
String DEFAULT_SOCIAL_PROCESS_URL = "/social";
}

View File

@ -104,10 +104,6 @@ public class UserController extends BaseController {
* @param userDto 用户信息
* @return R
*/
@ApiOperation(value = "更新用户详细信息", notes = "根据传过来的UserDto信息来更新用户详细信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "userDto", value = "用户详细实体user", required = true, dataType = "UserDto")
})
@PutMapping
public R<Boolean> userUpdate(@RequestBody UserDto userDto) {
SysUser user = userService.selectById(userDto.getUserId());
@ -136,6 +132,15 @@ public class UserController extends BaseController {
return userService.findUserByMobile(mobile);
}
/**
* 通过OpenId查询
* @param openId openid
* @return 对象
*/
@GetMapping("/findUserByOpenId/{openId}")
public UserVo findUserByOpenId(@PathVariable String openId){
return userService.findUserByOpenId(openId);
}
/**
* 分页查询用户
*

View File

@ -41,4 +41,12 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
* @return userVo
*/
UserVo selectUserVoByMobile(String mobile);
/**
* 通过openId查询用户信息
*
* @param openId openid
* @return userVo
*/
UserVo selectUserVoByOpenId(String openId);
}

View File

@ -79,4 +79,11 @@ public interface UserService extends IService<SysUser> {
* @return truefalse
*/
Boolean sendSmsCode(String mobile);
/**
* 通过openId查询用户
* @param openId openId
* @return 用户信息
*/
UserVo findUserByOpenId(String openId);
}

View File

@ -95,6 +95,18 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
return sysUserMapper.selectUserVoByMobile(mobile);
}
/**
* 通过openId查询用户
*
* @param openId openId
* @return 用户信息
*/
@Override
@Cacheable(value = "user_details_openid", key = "#openId")
public UserVo findUserByOpenId(String openId) {
return sysUserMapper.selectUserVoByOpenId(openId);
}
@Override
public Page selectWithRolePage(Query query) {
query.setRecords(sysUserMapper.selectUserVoPage(query, query.getCondition()));

View File

@ -34,7 +34,8 @@
<result column="rupdate_time" property="updateTime" />
</collection>
</resultMap>
<select id="selectUserVoByUsername" resultMap="userVoResultMap">
<sql id="selectUserVo">
SELECT
`user`.user_id,
`user`.username,
@ -55,33 +56,21 @@
sys_user AS `user`
LEFT JOIN sys_user_role AS ur ON ur.user_id = `user`.user_id
LEFT JOIN sys_role AS r ON r.role_id = ur.role_id
WHERE
`user`.username = #{username}
</sql>
<select id="selectUserVoByUsername" resultMap="userVoResultMap">
<include refid="selectUserVo"/>
WHERE `user`.username = #{username}
</select>
<select id="selectUserVoByMobile" resultMap="userVoResultMap">
SELECT
`user`.user_id,
`user`.username,
`user`.`password`,
`user`.salt,
`user`.introduction,
`user`.avatar,
`user`.create_time AS ucreate_time,
`user`.update_time AS uupdate_time,
`user`.del_flag AS udel_flag,
r.role_id,
r.role_name,
r.role_code,
r.role_desc,
r.create_time AS rcreate_time,
r.update_time AS rupdate_time
FROM
sys_user AS `user`
LEFT JOIN sys_user_role AS ur ON ur.user_id = `user`.user_id
LEFT JOIN sys_role AS r ON r.role_id = ur.role_id
WHERE
`user`.salt = #{mobile}
<include refid="selectUserVo"/>
WHERE `user`.introduction = #{mobile}
</select>
<select id="selectUserVoByOpenId" resultMap="userVoResultMap">
<include refid="selectUserVo"/>
WHERE `user`.salt = #{openId}
</select>
<select id="selectUserVoPage" resultMap="userVoResultMap" >

View File

@ -39,6 +39,7 @@
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>