第三方登录之接入Github

第三方登录
placeholder image
admin 发布于:2020-01-05 20:03:43
阅读:loading

基本介绍

这是本站实现的第6个第三方登录接入实现了,在此后很长一段时间应该不会再去实现其它的了(本来微信和360的登录也是可以有的,并不是接入难,而是资质审核麻烦),回顾这6种接入实现,还是往年实现的比较简单,都有官方提供的实现示例,而Gitee与Github则没有官方提供的实现,但它们的实现又是非常相似(非常非常),以至于我在实现Gitee后能够很轻易的实现基于Github的接入。本人实现这么多第三方站点的登录接入,主要有以下三个方面的意义:

(1)丰富个人的最佳实践,增加个人的技术领域深度;

(2)可以给本站增加一个新的来源的用户接入(让本站多放置一个按钮);

(3)提供友情接入接口,方便一些其它朋友站点(我接入多种来源的第三方登录,你们给我提供一个标识接入我即可,我将授权得到的用户ID、名称、头像、用户来源返回给你);

不多废话,整个接入过程建立在你已经拥有Github账号基础上,注册登录的不在介绍范围之内,详细介绍请见下文。

创建Github的第三方项目

(1)登录Github,点击右上角的个人头像处下拉菜单中的Settings --> Developer settings --> Oauth Apps,新建一个Oauth项目,录入对应的站点信息,由Github生成Client ID、Client Secret等,参考如下图所示:

image.png


image.png

(2)查看对应API文档,实际上它的API文档找起来也是非常费劲的,特别是我这种英文水平非常渣渣的选手来说,真的是非常非常费劲,以登录授权的接口文档为例,查找它对应的文档地址如下,输入一下地址并以这种跟路径往下找,带你一起找到登录的接口文档,输入网址:https://developer.github.com,参考如下:

image.png

image.png

image.png

最终的文档地址参考如下:https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/ ,由于对第三方授权的套路早已熟悉于心,尽管API文档是英文的,但也表示压力不大,就是耐着兴致一步一步往下走。

调用接入代码(跟Gitee高度相似)

package cn.chendd.third.login.controller;

import ...

/**
*
第三方登录 - Github登录
*
*
@author chendd
*
@date 2020/01/04 22:15
*/
@Api(tags = "Github账号登录")
@ApiSort(50)
@RequestMapping(value = "/third-login" , consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE , produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@RestController
@Slf4j
public class LoginGithubController extends ThirdLoginController {

   @GetMapping(value = "/github" , consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE , produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
   @ApiOperation(value = "github登录",notes = CALLBACK_NOTES)
   @ApiOperationSupport(order = 10)
   public String login(
           @ApiParam(value = "朋友站点标识")
           @RequestParam(required = false)
                   String friend) {
       String link = super.getLink(friend);
       GithubConfig config = super.getGithubConfig();
       GithubOauth oauth = new GithubOauth(config , link);
       return oauth.getAuthorize();
   }

   @GetMapping(value = "/githubCallback" , consumes = MediaType.ALL_VALUE , produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
   @ApiOperation(value = "github登录回调",notes = "第三方登录 - Github登录回调,被动发起请求")
   @ApiOperationSupport(order = 20)
   public ThirdUserResult githubCallback(
           @ApiParam(value = "github回调标识" , required = true) @RequestParam String code ,
           @ApiParam("朋友站点标识") @RequestParam(value = "state" , required = false) String redirect) throws Exception {
       //获取用户AccessToken
       
BaseResult baseTokenResult = this.getAccessToken(code, redirect);
       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()) ,
               //如果没有设置昵称名,则以登录名为准
               
StringUtils.isBlank(userInfoResult.getName()) ? userInfoResult.getLogin() : userInfoResult.getName() ,
               userInfoResult.getAvatarUrl() , EnumUserSource.Github , redirect);
   }

   /**
    *
根据用户授权code获取授权token
    */
   
private BaseResult getAccessToken(String code , String redirect) {
       GithubConfig config = super.getGithubConfig();
       CloseableHttpClient httpClient = HttpClientBuilder.create().build();
       List<NameValuePair> params = new ArrayList<>();
       params.add(new BasicNameValuePair("client_id" , config.getClientId()));
       params.add(new BasicNameValuePair("client_secret" , config.getClientSecret()));
       params.add(new BasicNameValuePair("code" , code));
       params.add(new BasicNameValuePair("redirect_uri" , config.getRedirectUri()));
       params.add(new BasicNameValuePair("state" , redirect));
       CloseableHttpResponse response = null;
       try {
           String url = config.getAuthorizeURL() + "/access_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());
           if(StringUtils.contains(result , "access_token=") && StringUtils.contains(result ,"token_type=")){
               return new SuccessResult<AccessTokenResult>(new AccessTokenResult(result));
           }
           return new ErrorResult(result);
       } catch (Exception e) {
           log.error("获取token请求异常" , e);
           return new ErrorResult(String.format("获取token请求异常,参考:%s" , e.getMessage()));
       } finally {
           CloseUtil.close(response , httpClient);
       }
   }

   /**
    *
根据授权token获取对应的用户详细信息
   
*/
   
private BaseResult geAccessTokentUserInfo(String accessToken){
       CloseableHttpClient httpClient = HttpClientBuilder.create().build();
       String url = super.getGithubConfig().getUserApiURL();
       /**
        *
经过验证,url=url?access_token=access_token的地址与从header中传递结果一致
       
*/
       
HttpGet httpGet = new HttpGet(url/* + "?access_token=" + accessToken*/);
       httpGet.setHeader("User-Agent" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0");
       httpGet.setHeader("Authorization" , "token " + accessToken);
       return super.consumesResponse(httpClient , httpGet , AccessTokenUserInfoResult.class);
   }

}
//super.consumesResponse
protected <T> BaseResult consumesResponse(CloseableHttpClient httpClient , HttpGet httpGet , Class<T> clazz){
   CloseableHttpResponse response = null;
   try {
       response = httpClient.execute(httpGet);
       String result = EntityUtils.toString(response.getEntity());
       return new SuccessResult<>(JSONObject.parseObject(result , clazz));
   } catch (Exception e) {
       log.error("根据token获取用户信息异常:" , e);
       return new ErrorResult(String.format("根据token获取用户信息异常,参考:%s" , e.getMessage()));
   } finally {
       CloseUtil.close(response , httpClient);
   }
}

授权过程

image.png

image.png

image.png

image.png

userNumber:用户ID

nickName:用户昵称

protrait:用户头像

redirect:友链回调地址

userSource:用户来源


 点赞


 发表评论

当前回复:作者

 评论列表


留言区