携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情
昨晚写了一篇关于 Oauth 2 的文章,今天就打算用它来实现一个第三方登录的案例。今日是实操啦~
但其实在此之前,我还写过 SpringBoot 整合 JustAuth 实现第三方登录 | gitee登录,但那次是借助 JustAuth 这个第三方包来实现的。
那我们如果自己实现该是如何勒?
一起来看看吧,其实没有你想象的那么难~
建议:
1、先阅读阅读关于 Oauth 2 的文章,更好的理解流程。
2、如果想一步到位,可以看看SpringBoot 整合 JustAuth 实现第三方登录 | gitee登录 这篇
大都数采取授权码模式的应用,在我们的应用中实现第三方登录时,大都一样的。很多共同的特性。
一、时序图
代码的实现,也是按照这些步骤所写的~
二、实现效果
补充说明:我是之前登录过了,所以没弹出那个认证授权页面,大家测试的时候,无痕测试就可以看出来啦。
授权页面如下:
三、Gitee第三方登录案例
项目的话,就创建一个普通的Springboot 项目即可,都是些常规依赖~
3.1、准备工作
在编写代码之前,记得先去Gitee创建一个第三方应用
点击个人头像-->设置-->侧边栏找到数据管理-->第三方应用-->创建一个第三方应用
上面的Client ID和 Client Secret待会也是要用到的。
主要填写的就是应用回调地址,这是在认证成功后,会调用的地址。
项目的整体结构
相关依赖
<parent>
<artifactId>spring-boot-dependencies</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.5.2</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.22</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
</dependencies>
配置文件
erver:
port: 8089
spring:
application:
name: springboot-thirdly-login
gitee:
client-id: 52ac7765xxxxx10b6c539b2c9d402
client-secret: 1198d5xxd8b0627cxxcd09cc818
redirect-uri: http://127.0.0.1:8089/oauth/gitee/callback
/**
* @description:
* @author: Ning Zaichun
* @date: 2022年08月28日 11:15
*/
@Data
@Component
@ConfigurationProperties(prefix = "gitee")
public class GiteeProperties {
private String clientId;
private String clientSecret;
private String redirectUri;
}
/**
* @description:
* @author: Ning Zaichun
* @date: 2022年08月28日 11:27
*/
public class ThirdlyLoginTypeConstant {
public final static String GITEE="gitee";
public final static String GITEE_URL="https://gitee.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code";
public final static String GITEE_OAUTH_TOKEN_URL="https://gitee.com/oauth/token";
}
3.2、service
/**
* @description:
* @author: Ning zaichun
* @date: 2022年08月28日 12:42
*/
public interface OauthService {
/**
* 根据传入的所选择的第三方登录类型,返回认证的url
* @param loginType 第三方登录类型
* @return
*/
String choiceLoginType(String loginType);
/**
*
* 根据用户的授权码和登录类型,获取第三方应用的 access_token
* @param loginType 第三方登录类型
* @param code 用户授权码
* @return
*/
String getOauthToken(String loginType, String code);
/**
* 获取授权的用户信息
* @param loginType 第三方登录类型
* @param accessToken 认证通过的令牌
* @return
*/
String getUserInfo(String loginType,String accessToken);
}
/**
* @description:
* @author: Ning Zaichun
* @date: 2022年08月28日 12:42
*/
@Service
public class OauthServiceImpl implements OauthService {
@Autowired
GiteeProperties giteeProperties;
@Override
public String choiceLoginType(String loginType) {
String url = "";
if (ThirdlyLoginTypeConstant.GITEE.equals(loginType)) {
url = ThirdlyLoginTypeConstant.GITEE_URL
.replace("{client_id}", giteeProperties.getClientId())
.replace("{redirect_uri}", giteeProperties.getRedirectUri());
}
return url;
}
@Override
public String getOauthToken(String loginType, String code) {
Map<String, Object> map = new HashMap<>();
String result = "";
if (ThirdlyLoginTypeConstant.GITEE.equals(loginType)) {
String url = ThirdlyLoginTypeConstant.GITEE_OAUTH_TOKEN_URL;
map.put("grant_type", "authorization_code");
map.put("code", code);
map.put("client_id", giteeProperties.getClientId());
map.put("client_secret", giteeProperties.getClientSecret());
map.put("redirect_uri", giteeProperties.getRedirectUri());
//发送get请求并接收响应数据
result = HttpUtil.createPost(url).form(map).execute().body();
}
return result;
}
@Override
public String getUserInfo(String loginType, String accessToken) {
String userInfo = "";
if (ThirdlyLoginTypeConstant.GITEE.equals(loginType)) {
String userInfoUrl = "https://gitee.com/api/v5/user?access_token=" + accessToken;
userInfo = HttpUtil.createGet(userInfoUrl).execute().body();
}
return userInfo;
}
}
3.3、controller
/**
* @description:
* @author: Ning zaichun
* @date: 2022年08月28日 11:12
*/
@Slf4j
@RequestMapping("/oauth")
@RestController
public class OauthController {
@Autowired
OauthService oauthService;
/**
* 1、用户点击使用 Gitee 作为第三方登录,后台重定向至gitee认证页面
*
* @param loginType
* @param response
* @throws IOException
*/
@GetMapping("/login/{loginType}")
public void thirdlyLogin(@PathVariable("loginType") String loginType, HttpServletResponse response) throws IOException {
String url = oauthService.choiceLoginType(loginType);
log.info(url);
response.sendRedirect(url);
}
/**
* 回调地址 这里就对应着我们在gitee创建第三方应用时填写的回调地址 这里会传回一个 code ,这个code 就是用户认证通过的授权码
* http://127.0.0.1:8089/oauth/gitee/callback?code=xxxxebe2a67ba13xxxx925615aa89041
* 其中 code 为用户授权码,由gitee授权服务器调用回调地址携带而来
*
* @param loginType
* @param code
* @param state 它并非是必填项,gitee调用回调地址时,其实没有携带这个参数,但是在oauth中有讲到,当时就写上去了。
* @return
*/
@ResponseBody
@GetMapping("/{loginType}/callback")
public String redirectUri(@PathVariable("loginType") String loginType, String code, String state) {
log.info("code==>{}", code);
//1、拿到这个code之后,我们要再向 gitee 服务起请求,获取 access_token 请注意这是一个post 请求
// https://gitee.com/oauth/token?grant_type=authorization_code&code={code}&client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}
String result = oauthService.getOauthToken(loginType, code);
ThirdlyResult thirdlyResult = (ThirdlyResult) JSON.parseObject(result, ThirdlyResult.class);
/**
* {
* "access_token":"f7d851b2cdxx1fd491b",
* "token_type":"bearer",
* "expires_in":86400,
* "refresh_token":"9f3098c7a8be09cd15xxcc38fb3dxxxb8e40f0800ced8",
* "scope":"user_info",
* "created_at":1661659283
* }
*/
/**
* 我这里只是测试获取用户信息的请求
* 如果需要实现登录的话,自己加上业务逻辑即可
*/
// 2、拿到 access_token 以后发送一个get 请求,获取用户信息
String userInfo = oauthService.getUserInfo(loginType, thirdlyResult.getAccessToken());
return userInfo;
}
}
在请求获取 access_token的接口会返回的数据格式,方便转换,就写了一下~
/**
* @description:
* @author: Ning zaichun
* @date: 2022年08月28日 12:18
*/
@Data
public class ThirdlyResult {
/**
* {
* "access_token":"f7xxxb71fd491b",
* "token_type":"bearer",
* "expires_in":86400,
* "refresh_token":"9f3098c7a8be09cdxxxxx53a2f69dccxxxx8e40f0800ced8",
* "scope":"user_info",
* "created_at":1661659283
* }
*/
private String accessToken;
private String tokenType;
private Long expiresIn;
private String refreshToken;
private String scope;
private String createAt;
}
3.4、小结
其实实现第三方登录其实也没有想象的那么难,把思路一步一步理清楚就可以了。
我也相信大家在看完文章后已经能够手动的实现第三方登录啦吧~
后记
尝试做一些自己喜欢的事情吧~
今天结束啦,周末也要结束啦~
放个收藏老久的可达鸭吧~
写于 2022 年 8 月 28日午后,作者:宁在春