Spring Security原始碼分析三:Spring Social實現QQ社交登入
社交登入又稱作社會化登入(Social Login),是指網站的使用者可以使用騰訊QQ、人人網、開心網、新浪微博、搜狐微博、騰訊微博、淘寶、豆瓣、MSN、Google等社會化媒體賬號登入該網站。
OAuth2.0的認證流程示意圖
- 請求第三方應用
- 第三方應用將使用者請求導向服務提供商
- 使用者同意授權
- 服務提供商返回code
- client根據code去服務提供商換取令牌
- 返回令牌
- 獲取使用者資訊
在標準的OAuth2協議中,1-6
步都是固定,只有最後一步,不通的服務提供商返回的使用者資訊是不同的。Spring Social
已經為我們封裝好了1-6
步。
使用Spring Social
準備工作
- 在qq互聯申請個人開發者,獲得appId和appKey或者使用 SpringForAll貢獻出來的
- 配置本地host 新增
127.0.0.1 www.ictgu.cn
- 資料庫執行以下sql
create table 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 UserConnection(userId, providerId, rank);
- 專案埠設定為
80
引入Spring Social 模組
模組 | 描述 |
---|---|
spring-social-core | 提供社交連線框架和OAuth 客戶端支援 |
spring-social-config | 提供Java 配置 |
spring-social-security | 社交安全的一些支援 |
spring-social-web | 管理web應用程式的連線 |
!--spring-social 相關-->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-web</artifactId>
</dependency>
目錄結構
- ‘api’ 定義api繫結的公共介面
- ‘config’ qq的一些配置資訊
- ‘connect’與服務提供商建立連線所需的一些類。
定義返回使用者資訊介面
public interface QQ {
/**
* 獲取使用者資訊
* @return
*/
QQUserInfo getUserInfo();
}
實現返回使用者資訊介面
@Slf4j
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
//http://wiki.connect.qq.com/openapi%E8%B0%83%E7%94%A8%E8%AF%B4%E6%98%8E_oauth2-0
private static final String QQ_URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
//http://wiki.connect.qq.com/get_user_info(access_token由父類提供)
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);
}
}
}
QQOAuth2Template處理qq返回的令牌資訊
@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;
}
}
QQServiceProvider連線服務提供商
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);
}
}
QQConnectionFactory連線服務提供商的工廠類
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {
public QQConnectionFactory(String providerId, String appId, String appSecret) {
super(providerId, new QQServiceProvider(appId, appSecret), new QQAdapter());
}
}
QQAdapter 適配spring Social預設的返回資訊
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());//openId 唯一標識
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) {
}
}
SocialConfig 社交配置主類
@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
/**
* 社交登入配類
*
* @return
*/
@Bean
public SpringSocialConfigurer merryyouSocialSecurityConfig() {
String filterProcessesUrl = SecurityConstants.DEFAULT_SOCIAL_QQ_PROCESS_URL;
MerryyouSpringSocialConfigurer configurer = new MerryyouSpringSocialConfigurer(filterProcessesUrl);
return configurer;
}
/**
* 處理註冊流程的工具類
* @param factoryLocator
* @return
*/
@Bean
public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator factoryLocator) {
return new ProviderSignInUtils(factoryLocator, getUsersConnectionRepository(factoryLocator));
}
}
QQAuthConfig 針對qq返回結果的一些操作
@Configuration
public class QQAuthConfig extends SocialAutoConfigurerAdapter {
@Autowired
private DataSource dataSource;
@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);
}
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource,
connectionFactoryLocator, Encryptors.noOpText());
if (myConnectionSignUp != null) {
repository.setConnectionSignUp(myConnectionSignUp);
}
return repository;
}
}
MerryyouSpringSocialConfigurer自定義登入和註冊連線
public class MerryyouSpringSocialConfigurer extends SpringSocialConfigurer {
private String filterProcessesUrl;
public MerryyouSpringSocialConfigurer(String filterProcessesUrl) {
this.filterProcessesUrl = filterProcessesUrl;
}
@Override
protected <T> T postProcess(T object) {
SocialAuthenticationFilter filter = (SocialAuthenticationFilter) super.postProcess(object);
filter.setFilterProcessesUrl(filterProcessesUrl);
filter.setSignupUrl("/register");
return (T) filter;
}
}
開啟SocialAuthenticationFilter過濾器
@Autowired
private SpringSocialConfigurer merryyouSpringSocialConfigurer;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin()//使用表單登入,不再使用預設httpBasic方式
.loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)//如果請求的URL需要認證則跳轉的URL
.loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)//處理表單中自定義的登入URL
.and()
.apply(merryyouSpringSocialConfigurer)
.and()
.authorizeRequests().antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM,
SecurityConstants.DEFAULT_REGISTER_URL,
"/register",
"/social/info",
"/**/*.js",
"/**/*.css",
"/**/*.jpg",
"/**/*.png",
"/**/*.woff2",
"/code/image")
.permitAll()//以上的請求都不需要認證
//.antMatchers("/").access("hasRole('USER')")
.and()
.csrf().disable()//關閉csrd攔截
;
//安全模組單獨配置
authorizeConfigProvider.config(http.authorizeRequests());
}
程式碼下載
相關推薦
Spring Security原始碼分析三:Spring Social實現QQ社交登入
社交登入又稱作社會化登入(Social Login),是指網站的使用者可以使用騰訊QQ、人人網、開心網、新浪微博、搜狐微博、騰訊微博、淘寶、豆瓣、MSN、Google等社會化媒體賬號登入該網站。 OAuth2.0的認證流程示意圖 請求第三方應用
Spring Security原始碼分析四:Spring Social實現微信社交登入
社交登入又稱作社會化登入(Social Login),是指網站的使用者可以使用騰訊QQ、人人網、開心網、新浪微博、搜狐微博、騰訊微博、淘寶、豆瓣、MSN、Google等社會化媒體賬號登入該網站。 前言 在上一章Spring-Security原始碼分析
Spring Security原始碼分析六:Spring Social社交登入原始碼解析
在Spring Security原始碼分析三:Spring Social實現QQ社交登入和Spring Security原始碼分析四:Spring Social實現微信社交登入這兩章中,我們使用Spring Social已經實現了國內最常用的QQ和微信社交
Spring Core Container 原始碼分析三:Spring Beans 初始化流程分析
前言 本文是筆者所著的 Spring Core Container 原始碼分析系列之一; 本篇文章主要試圖梳理出 Spring Beans 的初始化主流程和相關核心程式碼邏輯; 本文轉載自本人的部落格,傷神的部落格 http://www.shangyang.me/2017/
Spring Security原始碼分析十四:Spring Social 社交登入的繫結與解綁
社交登入又稱作社會化登入(Social Login),是指網站的使用者可以使用騰訊QQ、人人網、開心網、新浪微博、搜狐微博、騰訊微博、淘寶、豆瓣、MSN、Google等社會化媒體賬號登入該網站。 前言 在之前的Spring Social系列中,我
Spring Security原始碼分析十一:Spring Security OAuth2整合JWT
Json web token (JWT), 是為了在網路應用環境間傳遞宣告而執行的一種基於JSON的開放標準(RFC 7519).該token被設計為緊湊且安全的,特別適用於分散式站點的單點登入(SSO)場景。JWT的宣告一般被用來在身份提供者和服務提供者
Spring Security原始碼分析十六:Spring Security專案實戰
Spring Security是一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Contr
Spring Security原始碼分析十五:Spring Security 頁面許可權控制
Spring Security是一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Contr
Spring Security原始碼分析十二:Spring Security OAuth2基於JWT實現單點登入
單點登入(英語:Single sign-on,縮寫為 SSO),又譯為單一簽入,一種對於許多相互關連,但是又是各自獨立的軟體系統,提供訪問控制的屬性。當擁有這項屬性時,當用戶登入時,就可以獲取所有
Spring Core Container 源碼分析三:Spring Beans 初始化流程分析
turn raw time -c rri add 步驟 引用 lin 前言 本文是筆者所著的 Spring Core Container 源碼分析系列之一; 本篇文章主要試圖梳理出 Spring Beans 的初始化主流程和相關核心代碼邏輯; 本文轉載自本人的私人博客,傷神
Spring Boot+Spring Security專案開發(三):實現簡訊驗證碼登入
說在前面 博主最近會有很多專案跟大家一起分享,做完後會上傳github上的,希望讀友們能給博主提提意見哈哈 這個專案是第三方登入和安全方面的,關於後臺與app和網站的登入連線操作的實戰專案 各位如果可以就給我star哈哈謝謝啦 實
spring security 原始碼分析
表單登入 UsernamePasswordAuthenticationFilter /login Post 請求 會被這個過濾器攔截 如果為/login時 會經過 if (!this.requiresAuthentication(request, respo
5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析
一. AOP切面原始碼分析 原始碼分析分為三部分 1. 解析切面 2. 建立動態代理 3. 呼叫 原始碼的入口 原始碼分析的入口, 從註解開始: 元件的入口是一個註解, 比如啟用AOP的註解@EnableAspectJAutoProxy. 在註解的實現類裡面, 會有一個@Import("
Spring Boot 2.0(三):Spring Boot 開源軟件都有哪些?
Spring Boot 開源 2016年 Spring Boot 還沒有被廣泛使用,在網上查找相關開源軟件的時候沒有發現幾個,到了現在經過2年的發展,很多互聯網公司已經將 Spring Boot 搬上了生產,而使用 Spring Boot 的開源軟件在 Github/碼雲 上面已有不少,這篇文章就給大
spring boot 系列之三:spring boot 整合JdbcTemplate
closed com context boot pin pan url wired ace 前面兩篇文章我們講了兩件事情: 通過一個簡單實例進行spring boot 入門 修改spring boot 默認的服務端口號和默認context path 這篇文章我們來看下怎
Spring Boot 系統之三:Spring Boot 整合JdbcTemplate
前面兩篇文章我們講了兩件事情: 通過一個簡單例項進行Spring Boot 入門 修改Spring Boot 預設的服務埠號和預設context path 這篇文章我們來看下怎麼通過JdbcTemplate進行資料的持久化。 一、程式碼實現 1、修改pom.xml檔案
Spring Boot 2.0(三):Spring Boot 開源軟體都有哪些?
2016年 Spring Boot 還沒有被廣泛使用,在網上查詢相關開源軟體的時候沒有發現幾個,到了現在經過2年的發展,很多網際網路公司已經將 Spring Boot 搬上了生產,而使用 Spring Boot 的開源軟體在 Github/碼雲 上面已有不少,這篇文章就給大家介紹一下 Github/碼雲 上面
WebRTC原始碼分析三:視訊處理流程
文字介紹視訊的處理流程。圖1中顯示了兩路視訊會話視訊訊號流過程。 圖1 視訊流程示意圖 以一路視訊會話為例,主要分為以下幾個執行緒: 1)視訊源產生執行緒:Camera生產視訊畫面,封裝成視訊幀,以一定幀率投遞到下一個模組。; 2)採集執行緒:由Capturer負責採集視訊幀,並對視訊幀進行一定處理,如
ABP原始碼分析三:ABP Module
Abp是一種基於模組化設計的思想構建的。開發人員可以將自定義的功能以模組(module)的形式整合到ABP中。具體的功能都可以設計成一個單獨的Module。Abp底層框架提供便捷的方法整合每個Module.下圖是所有Abp自帶的module.AbpModule是所有Module的基類,其已經擁有了IIocMa
Spring Boot系列教程三:使用devtools實現熱部署
一.前言 Eclipse下使用spring-tool-suite外掛建立一個spring boot 工程,通過右鍵“Run As”--->"Spring Boot App"來啟動工程,這時當我們