第三方登录之接入Gitee

第三方登录
placeholder image
admin 发布于:2020-01-01 21:39:58
阅读:loading

新年第一天即将过去了,抓住尾巴,消灭2020年站里无文章的情况,特此分享出来这段时间接入码云(Gitee)成果。在过去的2019年增加了许多小伙伴的友情链接,也逐渐的丰富了网站里的功能,在过去的一年里,一直对Bootstrap持观望态度的我也不觉使用它有半年了,计划重新改版网站也是迟迟没有较大的动作,在系统管理功能完成后,的这个登录管理功能中我修改了对于第三方登录的实现,目前已经调整了新浪微博、百度和Gitee(新浪与百度的第三方登录目前本站无法继续使用了),本次则是基于Gitee的一些研究分享,提供参考。

应用设置

其实第三方登录的接入也有好多成型的接入方案,但我觉得作为一个开发者还是自行研究实现较为科学,毕竟任何一个实现抛开是否存在价值不说,在整个过程中的问题及解决的过程是一个含有许多滋味的,同时我这个版本的接入上线时也等效于我实现一个统一接入平台,大家可以通过一个地址实现我所有的第三方登录,即:你请求我的接口地址,我给你提供完整的接入实现,乘着余温(刚刚把代码示例全部调通),对Gitee的登录接入列一些非专业的总结:

(1)在Gitee中创建需要接入项目的过程比较简单,修改即时生效,填写应用名称、Logo、回调地址、介绍等信息就可以了,不需要再多的其它资料(开发者认证等等),参考如下图所示:

image.png

(右上角个人头像处-->个人主页-->个人设置-->第三方应用)

(2)它的接口文档(接口示例、具体说明)相比较其它的一些接入来说资源较少,或者说从应用详情或者是其它设置中均为直观的找到API文档相关的介绍地址,经过一番搜索后找到的地址如下:具体接口API文档地址:https://gitee.com/api/v5/swagger#/getV5ReposOwnerRepoStargazers?ex=no,OAuth接入地址:https://gitee.com/api/v5/oauth_doc#/,上述地址中有官方提供的较为详细的描述,无代码参考,如果自行编写代码需要拿出你的勇气,迈出你编写的步伐,步骤参考:

image.png

(3)整个接入过程比本站已经实现的其它接入多了一个步骤,它在回调我们项目的时候返回了授权码,我们需要根据授权码再获取Token,最后再根据Token获取具体的对应接口,如本站获取的是用户基本信息,与其它接入参数一致,只获取用户ID、用户昵称、用户头像这三个参数,附上官方提供的接入流程图,参考如下:

image.png

(4)Gitee提供了大量的接口示例,实际上在基本上没有加入任何群,完全个人摸索的情况下,找了许久才找到它的登录接口的,地址为:https://gitee.com/api/v5/oauth_doc#/,参考如下图所示:

image.png

参考代码

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()是一个从数据库获取配置参数的实现,具体参数参考如下:

image.png

示例演示

(1)请求本站授权

image.png

(2)登录账号并授权

image.png



image.png

(3)返回授权后的用户信息

image.png

听着音乐,一边想着一边写着,想到哪里写到哪里,完全自我表达,有意见或者建议可留言。


 点赞


 发表评论

当前回复:作者

 评论列表


留言区