第三方登录之接入Gitee
第三方登录admin 发布于:2020-01-01 21:39:58
阅读:loading
新年第一天即将过去了,抓住尾巴,消灭2020年站里无文章的情况,特此分享出来这段时间接入码云(Gitee)成果。在过去的2019年增加了许多小伙伴的友情链接,也逐渐的丰富了网站里的功能,在过去的一年里,一直对Bootstrap持观望态度的我也不觉使用它有半年了,计划重新改版网站也是迟迟没有较大的动作,在系统管理功能完成后,的这个登录管理功能中我修改了对于第三方登录的实现,目前已经调整了新浪微博、百度和Gitee(新浪与百度的第三方登录目前本站无法继续使用了),本次则是基于Gitee的一些研究分享,提供参考。
其实第三方登录的接入也有好多成型的接入方案,但我觉得作为一个开发者还是自行研究实现较为科学,毕竟任何一个实现抛开是否存在价值不说,在整个过程中的问题及解决的过程是一个含有许多滋味的,同时我这个版本的接入上线时也等效于我实现一个统一接入平台,大家可以通过一个地址实现我所有的第三方登录,即:你请求我的接口地址,我给你提供完整的接入实现,乘着余温(刚刚把代码示例全部调通),对Gitee的登录接入列一些非专业的总结:
(1)在Gitee中创建需要接入项目的过程比较简单,修改即时生效,填写应用名称、Logo、回调地址、介绍等信息就可以了,不需要再多的其它资料(开发者认证等等),参考如下图所示:
(右上角个人头像处-->个人主页-->个人设置-->第三方应用)
(2)它的接口文档(接口示例、具体说明)相比较其它的一些接入来说资源较少,或者说从应用详情或者是其它设置中均为直观的找到API文档相关的介绍地址,经过一番搜索后找到的地址如下:具体接口API文档地址:https://gitee.com/api/v5/swagger#/getV5ReposOwnerRepoStargazers?ex=no,OAuth接入地址:https://gitee.com/api/v5/oauth_doc#/,上述地址中有官方提供的较为详细的描述,无代码参考,如果自行编写代码需要拿出你的勇气,迈出你编写的步伐,步骤参考:
(3)整个接入过程比本站已经实现的其它接入多了一个步骤,它在回调我们项目的时候返回了授权码,我们需要根据授权码再获取Token,最后再根据Token获取具体的对应接口,如本站获取的是用户基本信息,与其它接入参数一致,只获取用户ID、用户昵称、用户头像这三个参数,附上官方提供的接入流程图,参考如下:
(4)Gitee提供了大量的接口示例,实际上在基本上没有加入任何群,完全个人摸索的情况下,找了许久才找到它的登录接口的,地址为:https://gitee.com/api/v5/oauth_doc#/,参考如下图所示:
package cn.chendd.third.login.controller;
import ...;
/**
* 第三方登录 - 接入Gitee码云登录
*
* @author chendd
* @date 2019/12/21 21:55
*/
@Api(tags = "Gitee账号登录")
@ApiSort(40)
@RequestMapping(value = "/third-login")
@RestController
@Slf4j
public class LoginGiteeController extends ThirdLoginController{
@GetMapping(value = "/gitee" , consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE , produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ApiOperation(value = "Gitee登录",notes = CALLBACK_NOTES)
@ApiOperationSupport(order = 10)
public String login(
@ApiParam(value = "朋友站点标识")
@RequestParam(required = false)
String friend) throws ValidateDataException, WeiboException {
String link = super.getLink(friend);
String clientId = super.getGiteeConfig().get("client_id");
String redirectURI = super.getGiteeConfig().get("redirect_uri");
String authorizeURL = super.getGiteeConfig().get("authorizeURL");
GieteeOauth oauth = new GieteeOauth(authorizeURL , clientId , redirectURI , link);
String authorize = oauth.getAuthorize();
return authorize;
}
@GetMapping(value = "/giteeCallback" , consumes = MediaType.ALL_VALUE , produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ApiOperation(value = "Gitee登录回调",notes = "第三方登录 - Gitee登录回调,被动发起请求")
@ApiOperationSupport(order = 20)
public ThirdUserResult giteeCallback(
@ApiParam(value = "gitee回调标识" , required = true) @RequestParam String code ,
@ApiParam("朋友站点标识") @RequestParam(value = "state" , required = false) String redirect) throws Exception {
//获取用户AccessToken
BaseResult baseTokenResult = this.getAccessToken(code);
String tokenResult = baseTokenResult.getResult();
if(! EnumResult.success.name().equals(tokenResult)){
throw new ValidateDataException(baseTokenResult.getMessage());
}
//获取用户
AccessTokenResult tokenResultData = (AccessTokenResult) baseTokenResult.getData();
BaseResult baseTokenUserResult = this.geAccessTokentUserInfo(tokenResultData.getAccessToken());
String tokenUserResult = baseTokenUserResult.getResult();
if(! EnumResult.success.name().equals(tokenUserResult)){
throw new ValidateDataException(baseTokenUserResult.getMessage());
}
AccessTokenUserInfoResult userInfoResult = (AccessTokenUserInfoResult) baseTokenUserResult.getData();
return new ThirdUserResult(String.valueOf(userInfoResult.getId()) , userInfoResult.getName() ,
userInfoResult.getAvatarUrl() , EnumUserSource.Gitee , redirect);
}
/**
* 根据用户授权code获取授权token
*/
private BaseResult getAccessToken(String code) throws Exception {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("grant_type" , "authorization_code"));
params.add(new BasicNameValuePair("code" , code));
params.add(new BasicNameValuePair("client_id" , super.getGiteeConfig().get("client_id")));
params.add(new BasicNameValuePair("redirect_uri" ,
super.getGiteeConfig().get("redirect_uri")));
params.add(new BasicNameValuePair("client_secret" , super.getGiteeConfig().get("client_secret")));
CloseableHttpResponse response = null;
try {
String url = super.getGiteeConfig().get("authorizeURL") + "/oauth/token";
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("User-Agent" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0");
httpPost.setEntity(new UrlEncodedFormEntity(params));
response = httpClient.execute(httpPost);
String result = EntityUtils.toString(response.getEntity());
return new SuccessResult<AccessTokenResult>(JSONObject.parseObject(result , AccessTokenResult.class));
} catch (Exception e) {
return new ErrorResult<>("获取token请求异常,参考:%s" , e.getMessage());
} finally {
CloseUtil.close(response , httpClient);
}
}
/**
* 根据授权token获取对应的用户详细信息
*/
private BaseResult geAccessTokentUserInfo(String accessToken) throws Exception {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
String url = super.getGiteeConfig().get("authorizeURL") + "/api/v5/user?access_token=" + accessToken;
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("User-Agent" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0");
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpGet);
String result = EntityUtils.toString(response.getEntity());
return new SuccessResult<AccessTokenUserInfoResult>(JSONObject.parseObject(result , AccessTokenUserInfoResult.class));
} catch (Exception e) {
log.error("发送token请求异常:" , e);
return new ErrorResult(String.format("获取token请求异常,参考:%s" , e.getMessage()));
} finally {
CloseUtil.close(response , httpClient);
}
}
}
package cn.chendd.third.login.custom;
import ...
/**
* 接入gitee登录实现
*
* @author chendd
* @date 2019/12/29 20:59
*/
@Slf4j
public class GieteeOauth {
private StringBuilder urlBuilder;
public GieteeOauth(String authorizeURL , String clientId , String redirectURI , String link) {
urlBuilder = new StringBuilder();
try {
urlBuilder.append(authorizeURL)
.append("/oauth/authorize?client_id=").append(clientId)
.append("&redirect_uri=").append(URLEncoder.encode(redirectURI , Charsets.UTF_8.name()))
.append("&response_type=code");
if(StringUtils.isNotBlank(link)){
urlBuilder.append("&state=").append(link);
}
} catch (UnsupportedEncodingException e) {
log.error("构造gitee登录出现错误:" , e);
}
}
public GieteeOauth(String authorizeURL, String code , String clientId , String redirectURI , String clientSecret) {
urlBuilder = new StringBuilder();
try {
urlBuilder.append(authorizeURL)
.append("/oauth/token?grant_type=authorization_code&code=").append(code)
.append("&client_id=").append(clientId)
.append("&redirect_uri=").append(URLEncoder.encode(redirectURI , Charsets.UTF_8.name()))
.append("&client_secret=").append(clientSecret);
} catch (UnsupportedEncodingException e) {
log.error("构造gitee登录出现错误:" , e);
}
}
public String getAuthorize() {
return urlBuilder.toString();
}
public String getToken(){
return urlBuilder.toString();
}
}
上述代码一定无法通过编译,直接从系统代码里摘出来的,大的方向没问题,只是有细节上的逻辑包装,后面随着博客的开源全部开源,super.getGiteeConfig()是一个从数据库获取配置参数的实现,具体参数参考如下:
(1)请求本站授权
(2)登录账号并授权
(3)返回授权后的用户信息
听着音乐,一边想着一边写着,想到哪里写到哪里,完全自我表达,有意见或者建议可留言。
点赞