Spring Boot 快速集成第三方登錄功能
此 demo 主要演示 Spring Boot 項目如何使用 史上最全的第三方登錄工具 - JustAuth 實現第三方登錄,包括 QQ 登錄、GitHub 登錄、微信登錄、谷歌登錄、微軟登錄、小米登錄、企業微信登錄。 通過 justauth-spring-boot-starter 快速集成,好嗨喲~ JustAuth,如你所見,它僅僅是一個第三方授權登錄的工具類庫,它可以讓我們脫離繁瑣的第三方登錄 SDK,讓登錄變得So easy!
PS: 本人十分幸運的參與到了這個 SDK 的開發,主要開發了 QQ 登錄、微信登錄、小米登錄、微軟登錄、谷歌登錄這 |
1. Demo
完整版 demo:https://github.com/justauth/justauth-spring-boot-starter-demo
2. 更新日志
3. 快速開始
3.1. 基礎配置
- 引用依賴
<dependency> <groupId>com.xkcoding.justauth</groupId> <artifactId>justauth-spring-boot-starter</artifactId> <version>1.3.4</version> </dependency>
- 添加配置,在
application.yml
中添加配置配置信息
注意:
justauth.type
節點的配置,請根據項目實際情況選擇,多余的可以刪除- 如果使用 QQ 登錄,並且需要獲取
unionId
,則必須傳union-id
配置,並置為true
- 如果使用支付寶登錄,必傳
alipay-public-key
- 如果使用 Stack Overflow 登錄,必傳
stack-overflow-key
- 如果使用企業微信登錄,必傳
agent-id
- 如果使用 CODING 登錄,必傳
coding-group-name
justauth: enabled: true type: QQ: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/qq/callback union-id: false WEIBO: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/weibo/callback GITEE: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/gitee/callback DINGTALK: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/dingtalk/callback BAIDU: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/baidu/callback CSDN: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/csdn/callback CODING: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/coding/callback coding-group-name: xx OSCHINA: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/oschina/callback ALIPAY: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/alipay/callback alipay-public-key: MIIB**************DAQAB WECHAT_OPEN: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/wechat_open/callback WECHAT_MP: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/wechat_mp/callback WECHAT_ENTERPRISE: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/wechat_enterprise/callback agent-id: 1000002 TAOBAO: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/taobao/callback GOOGLE: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/google/callback FACEBOOK: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/facebook/callback DOUYIN: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/douyin/callback LINKEDIN: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/linkedin/callback MICROSOFT: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/microsoft/callback MI: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/mi/callback TOUTIAO: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/toutiao/callback TEAMBITION: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/teambition/callback RENREN: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/renren/callback PINTEREST: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/pinterest/callback STACK_OVERFLOW: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/stack_overflow/callback stack-overflow-key: asd*********asd HUAWEI: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/huawei/callback KUJIALE: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/kujiale/callback GITLAB: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/gitlab/callback MEITUAN: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/meituan/callback ELEME: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/eleme/callback TWITTER: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/twitter/callback cache: type: default
- 然后就開始玩耍吧~
@Slf4j @RestController @RequestMapping("/oauth") @RequiredArgsConstructor(onConstructor_ = @Autowired) public class TestController { private final AuthRequestFactory factory; @GetMapping public List<String> list() { return factory.oauthList(); } @GetMapping("/login/{type}") public void login(@PathVariable String type, HttpServletResponse response) throws IOException { AuthRequest authRequest = factory.get(type); response.sendRedirect(authRequest.authorize(AuthStateUtils.createState())); } @RequestMapping("/{type}/callback") public AuthResponse login(@PathVariable String type, AuthCallback callback) { AuthRequest authRequest = factory.get(type); AuthResponse response = authRequest.login(callback); log.info("【response】= {}", JSONUtil.toJsonStr(response)); return response; } }
/** * <p> * 第三方登錄 Controller * </p> * * @package: com.xkcoding.oauth.controller * @description: 第三方登錄 Controller * @author: yangkai.shen * @date: Created in 2019-05-17 10:07 * @copyright: Copyright (c) 2019 * @version: V1.0 * @modified: yangkai.shen */ @Slf4j @RestController @RequestMapping("/oauth") @RequiredArgsConstructor(onConstructor_ = @Autowired) public class OauthController { private final AuthRequestFactory factory; /** * 登錄類型 */ @GetMapping public Map<String, String> loginType() { List<String> oauthList = factory.oauthList(); return oauthList.stream().collect(Collectors.toMap(oauth -> oauth.toLowerCase() + "登錄", oauth -> "http://oauth.xkcoding.com/demo/oauth/login/" + oauth.toLowerCase())); } /** * 登錄 * * @param oauthType 第三方登錄類型 * @param response response * @throws IOException */ @RequestMapping("/login/{oauthType}") public void renderAuth(@PathVariable String oauthType, HttpServletResponse response) throws IOException { AuthRequest authRequest = factory.get(getAuthSource(oauthType)); response.sendRedirect(authRequest.authorize(oauthType + "::" + AuthStateUtils.createState())); } /** * 登錄成功后的回調 * * @param oauthType 第三方登錄類型 * @param callback 攜帶返回的信息 * @return 登錄成功后的信息 */ @RequestMapping("/{oauthType}/callback") public AuthResponse login(@PathVariable String oauthType, AuthCallback callback) { AuthRequest authRequest = factory.get(getAuthSource(oauthType)); AuthResponse response = authRequest.login(callback); log.info("【response】= {}", JSONUtil.toJsonStr(response)); return response; } private AuthSource getAuthSource(String type) { if (StrUtil.isNotBlank(type)) { return AuthSource.valueOf(type.toUpperCase()); } else { throw new RuntimeException("不支持的類型"); } } }
3.2. 緩存配置
starter 內置了2種緩存實現,一種是上面的默認實現,另一種是基於 Redis 的緩存實現。
當然了,你也可以自定義實現你自己的緩存。
3.2.1. 默認緩存實現
在配置文件配置如下內容即可
justauth: cache: type: default
3.2.2. Redis 緩存實現
1.添加 Redis 相關依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 對象池,使用redis時必須引入 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
justauth: cache: type: redis # 緩存前綴,目前只對redis緩存生效,默認 JUSTAUTH::STATE:: prefix: '' # 超時時長,目前只對redis緩存生效,默認3分鍾 timeout: 1h spring: redis: host: localhost # 連接超時時間(記得添加單位,Duration) timeout: 10000ms # Redis默認情況下有16個分片,這里配置具體使用的分片 # database: 0 lettuce: pool: # 連接池最大連接數(使用負值表示沒有限制) 默認 8 max-active: 8 # 連接池最大阻塞等待時間(使用負值表示沒有限制) 默認 -1 max-wait: -1ms # 連接池中的最大空閑連接 默認 8 max-idle: 8 # 連接池中的最小空閑連接 默認 0 min-idle: 0
3.2.3. 自定義緩存實現
1.配置文件配置如下內容
justauth:
cache:
type: custom
AuthStateCache
接口
/** * <p> * 自定義緩存實現 * </p> * * @author yangkai.shen * @date Created in 2019/8/31 12:53 */ public class MyAuthStateCache implements AuthStateCache { /** * 存入緩存 * * @param key 緩存key * @param value 緩存內容 */ @Override public void cache(String key, String value) { // TODO: 自定義存入緩存 } /** * 存入緩存 * * @param key 緩存key * @param value 緩存內容 * @param timeout 指定緩存過期時間(毫秒) */ @Override public void cache(String key, String value, long timeout) { // TODO: 自定義存入緩存 } /** * 獲取緩存內容 * * @param key 緩存key * @return 緩存內容 */ @Override public String get(String key) { // TODO: 自定義獲取緩存內容 return null; } /** * 是否存在key,如果對應key的value值已過期,也返回false * * @param key 緩存key * @return true:存在key,並且value沒過期;false:key不存在或者已過期 */ @Override public boolean containsKey(String key) { // TODO: 自定義判斷key是否存在 return false; } }
JustAuthConfig
/** * <p> * 自定義緩存裝配 * </p> * * @author yangkai.shen * @date Created in 2019/8/31 12:29 */ @Configuration public class AuthStateConfiguration { @Bean public AuthStateCache authStateCache() { return new MyAuthStateCache(); } }
3.3. 自定義第三方平台配置
1.創建自定義的平台枚舉類
/** * <p> * 擴展的自定義 source * </p> * * @author yangkai.shen * @date Created in 2019/10/9 14:14 */ public enum ExtendSource implements AuthSource { /** * 測試 */ TEST { /** * 授權的api * * @return url */ @Override public String authorize() { return "http://authorize"; } /** * 獲取accessToken的api * * @return url */ @Override public String accessToken() { return "http://accessToken"; } /** * 獲取用戶信息的api * * @return url */ @Override public String userInfo() { return null; } /** * 取消授權的api * * @return url */ @Override public String revoke() { return null; } /** * 刷新授權的api * * @return url */ @Override public String refresh() { return null; } } }
/** * <p> * 測試用自定義擴展的第三方request * </p> * * @author yangkai.shen * @date Created in 2019/10/9 14:19 */ public class ExtendTestRequest extends AuthDefaultRequest { public ExtendTestRequest(AuthConfig config) { super(config, ExtendSource.TEST); } public ExtendTestRequest(AuthConfig config, AuthStateCache authStateCache) { super(config, ExtendSource.TEST, authStateCache); } /** * 獲取access token * * @param authCallback 授權成功后的回調參數 * @return token * @see AuthDefaultRequest#authorize() * @see AuthDefaultRequest#authorize(String) */ @Override protected AuthToken getAccessToken(AuthCallback authCallback) { return AuthToken.builder().openId("openId").expireIn(1000).idToken("idToken").scope("scope").refreshToken("refreshToken").accessToken("accessToken").code("code").build(); } /** * 使用token換取用戶信息 * * @param authToken token信息 * @return 用戶信息 * @see AuthDefaultRequest#getAccessToken(AuthCallback) */ @Override protected AuthUser getUserInfo(AuthToken authToken) { return AuthUser.builder().username("test").nickname("test").gender(AuthUserGender.MALE).token(authToken).source(this.source.toString()).build(); } /** * 撤銷授權 * * @param authToken 登錄成功后返回的Token信息 * @return AuthResponse */ @Override public AuthResponse revoke(AuthToken authToken) { return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).msg(AuthResponseStatus.SUCCESS.getMsg()).build(); } /** * 刷新access token (續期) * * @param authToken 登錄成功后返回的Token信息 * @return AuthResponse */ @Override public AuthResponse refresh(AuthToken authToken) { return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(AuthToken.builder().openId("openId").expireIn(1000).idToken("idToken").scope("scope").refreshToken("refreshToken").accessToken("accessToken").code("code").build()).build(); } }
justauth: enabled: true extend: enum-class: com.xkcoding.justauthspringbootstarterdemo.extend.ExtendSource config: TEST: request-class: com.xkcoding.justauthspringbootstarterdemo.extend.ExtendTestRequest client-id: xxxxxx client-secret: xxxxxxxx redirect-uri: http://oauth.xkcoding.com/demo/oauth/test/callback
4. http 代理配置
修改配置文件,增加如下配置:
justauth: http-config: timeout: 30000 proxy: GOOGLE: type: HTTP hostname: 127.0.0.1 port: 10080
http-config
節點下添加相關配置,格式參考上面示例
5. 自定義 Scopes
修改配置文件,增加如下配置:
justauth: enabled: true type: QQ: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: http://oauth.xkcoding.com/demo/oauth/qq/callback union-id: false scopes: - get_user_info - xxxx
me.zhyd.oauth.enums.scope
包下查看各個渠道所支持的 scopes,當然你可以不配置該項,JustAuth 會默認添加上一些基礎 scope
6. 附錄
6.1. justauth
配置列表
java.util.Map<me.zhyd.oauth.config.AuthSource,com.xkcoding.justauth.autoconfigure.JustAuthProperties.JustAuthHttpConfig>
com.xkcoding.justauth.autoconfigure.CacheProperties
com.xkcoding.justauth.autoconfigure.ExtendProperties
屬性名 | 類型 | 默認值 | 可選項 | 描述 |
|
|
true | true/false |
是否啟用 JustAuth |
|
|
無 |
JustAuth 配置 |
|
|
無 |
http 相關配置 |
||
|
JustAuth緩存配置 |
|||
|
無 |
JustAuth第三方平台配置 |
justauth.type
配置列表
屬性名 | 描述 |
justauth.type.keys | justauth.type 是 Map 格式的,key 的取值請參考AuthDefaultSource |
justauth.type.keys.values | justauth.type 是 Map 格式的,value 的取值請參考 AuthConfig |
justauth.type.keys.values
所有可選配置如下:
屬性名 |
描述 |
備注 |
client-id |
客戶端id,對應各平台的appKey |
必填 |
client-secret |
客戶端Secret,對應各平台的appSecret |
必填 |
redirect-uri |
登錄成功后的回調地址 |
必填 |
alipay-public-key |
支付寶公鑰 |
當使用支付寶登錄時, 該值必填,對應“RSA2(SHA256)密鑰”中的“支付寶公鑰” |
union-id |
是否需要申請unionid |
|
stack-overflow-key |
Stack Overflow Key |
當使用Stack Overflow登錄時, 該值必填 |
agent-id |
企業微信,授權方的網頁應用ID |
當使用企業微信登錄時, 該值必填 |
coding-group-name |
團隊域名前綴 |
使用 Coding 登錄時, 該值必填 |
justauth.httpConfig
配置列表
屬性名 |
描述 |
|
justauth.httpConfig.keys |
||
justauth.httpConfig.keys.values |
justauth.type 是 Map 格式的,value 的取值請參考 JustAuthProperties.JustAuthHttpConfig |
justauth.httpConfig.keys.values
所有可選配置如下:
屬性名 |
描述 |
備注 |
timeout |
請求超時時間 |
|
proxy |
代理的相關配置,針對國外平台,需要配置代理 |
必填 |
justauth.httpConfig.proxy
所有可選配置如下:
屬性名 |
描述 |
備注 |
type |
代理類型,可選值:HTTP、DIRECT、SOCKS,默認為 HTTP |
|
hostname |
代理 IP 地址 |
|
port |
代理端口 |
justauth.cache
配置列表
屬性名 |
類型 |
默認值 |
可選項 |
描述 |
justauth.cache.type |
com.xkcoding.justauth.autoconfigure.CacheProperties.CacheType |
default |
default/redis/custom |
緩存類型,default使用JustAuth默認的緩存實現,redis使用默認的redis緩存實現,custom用戶自定義緩存實現 |
justauth.cache.prefix |
java.lang.String |
JUSTAUTH::STATE:: |
緩存前綴,目前只對redis緩存生效,默認 JUSTAUTH::STATE:: |
|
justauth.cache.timeout |
java.time.Duration |
3分鍾 |
超時時長,目前只對redis緩存生效,默認3分鍾 |
justauth.extend
配置列表
屬性名 |
類型 |
默認值 |
可選項 |
描述 |
justauth.extend.enum-class |
Class<? extends AuthSource> |
無 |
枚舉類全路徑 |
|
justauth.extend.config |
java.util.Map<String, ExtendRequestConfig> |
無 |
對應配置信息 |
justauth.extend.config
配置列表
屬性名 |
類型 |
默認值 |
可選項 |
描述 |
justauth.extend.config.keys |
java.lang.String |
無 |
key 必須在 justauth.extend.enum-class 配置的枚舉類中聲明 |
|
justauth.extend.config.values |
com.xkcoding.justauth.autoconfigure.ExtendProperties.ExtendRequestConfig |
無 |
value 就是 AuthConfig 的子類,增加了一個 request-class 屬性配置請求的全類名,具體參考類ExtendProperties.ExtendRequestConfig |
2. SNAPSHOT版本
如果需要體驗快照版本,可以在你的 pom.xml
進行如下配置:
<repositories> <!--阿里雲私服--> <repository> <id>aliyun</id> <name>aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </repository> <!--中央倉庫--> <repository> <id>oss</id> <name>oss</name> <url>http://oss.sonatype.org/content/repositories/snapshots</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories>
參考:
https://blog.csdn.net/andyliulin/article/details/100008643
https://github.com/justauth/justauth-spring-boot-starter
https://github.com/justauth/justauth-spring-boot-starter-demo