Spring Boot 快速集成第三方登录功能
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 会默认添加上一些基础 scope6. 附录
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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析