作者:陈东东
地址:https://chendd.cn/information/viewInformation/experienceShare/334.a
在系统管理功能完成后,的这个登录管理功能中我修改了对于第三方登录的实现,目前已经调整了新浪微博、百度和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@Slf4jpublic 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 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(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(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 */@Slf4jpublic 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)返回授权后的用户信息

