1. OAuth2.0 简介

全称:Open Authorization

OAuth(Open Authorization)是一个关于授权(authorization)的敞开网络标准,答运用户授权第三方 运用拜访他们存储在另外的服务供给者上的信息,而不需求将用户名和暗码供给给第三方。 OAuth在全世界得到广泛运用,现在的版本是2.0版

特点:

  1. 简略:不管是OAuth服务供给者仍是运用开发者,都很易于了解和运用
  2. 安全:没有涉及到用户密钥等信息,更安全灵活
  3. 敞开:任何服务供给商都能够完结OAuth,任何软件开发上商都能够运用OAuth

四种形式

  • 授权码形式(Authorization Code):OAuth2 中现在最安全最杂乱也是最常用的形式

  • 隐式授权形式(Implicit Grant):

  • 用户命暗码形式(Resource Owner Passowrd Credentials grant ):暗码凭据授权

  • 客户端形式(Client Credientials Grant):客户端授权

2. OAuth2.0 长处

OAuth登录的长处

  • 第三方登录简略便利,面对不同渠道不同的用户名和暗码的问题,第三方登 录正好解决这个问题,几乎能够直接一个账号搞定一切
  • 第三方登录还能够将自己在某个运用的动态信息同步到当前运用下,无需再为每个运用重新写个人材料
  • 第三方登录有很多材料信息能够共用(比方头像和昵称),一般关于灵敏材料如手机、邮箱是第三方渠道是不会供给的,所以安全信息能够放心

OAuth为企业带来的价值

  • 简化登录过程,降低注册门槛,更能获取海量用户,有用降低了用户的流失。本地注册的稳定+第三方登 录的快捷才是最合适的登录计划

  • 第三方登录接入后,运用可直接获取用户昵称、头像、用户D等信息,便利产品获取用户的基本材料,削减产品规划本钱

  • 现在市面上的短信验证码的价钱约在0.05元左右,当用户挑选运用第三方登录时,可有用削减产品的登 录本钱

OAuth为第三方供给商带来的价值

  • 增加用户对渠道(QQ\微信\支付宝\Google)的依靠,用户越多运用本渠道的第三方登录,就代表着渠道对该用户的粘性越高

  • 获得更广泛的影响力,只要用户运用供给第三方登录的运用,那么这个供给第三方登录的品牌就会被用户阅读,有利于对渠道的拉新和促活

3 OAuth2.0 人物

人物 效果
客户端 本身不存储资源,需求经过资源具有者去恳求资源服务器的资源。APP,游戏,影视网站…
资源具有者 一般为用户,也能够是运用程序,即资源的
授权服务器 用于服务供给商对资源具有者的身份进行认证、对拜访资源进行授权发放授权码(code)
认证成功后经过授权码恳求发放令牌(token),作为客户端拜访资源服务器的凭据。
资源服务器 存储资源的服务器,比方微信端存储的用户信息

4 OAuth2.0 授权码形式

OAuth2 中现在最安全最杂乱也是最常用的形式,履行流程如下

OAuth2.0 第三方授权登录(微信和Gitee)

5 二维码生成

5.1 依靠要求

<!--Hutool:JaVa东西包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK办法进行封装,组成各种Ui东西类。-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.15</version>
</dependency>
<!--生成二维码-->
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.3.3</version>
</dependency>

5.2 生成二维码示例

  • 一般生成:四个参数:Url,宽,高,输出路径
QrCodeUtil.generate(
    "https://warriorsgo.netlify.app",300,300, FileUtil.file("d:/qrcode.jpg")
);
  • 装备生成:装备信息较多时推荐将装备信息写入装备目标再生成
//装备参数较多时运用装备生成
QrConfig config = new QrConfig(1000, 1000);//新建装备文件并指定宽高
config.setErrorCorrection(ErrorCorrectionLevel.Q);//指定纠错等级
QrCodeUtil.generate(
    "https://warriorsgo.netlify.app",config, FileUtil.file("d:/qrcode.jpg")
);

其间:纠错等级 ErrorCorrectionLevel有四个等级:L,M,Q,H

从左到右:纠错等级进步,即便被遮挡一小部分也能识别成功,单位像素块面积减小,像素块数量增多

  • 其他装备
装备办法 效果
setBackColor(Color.XXX) 设置背景颜色
setForeColor(Color.XXX) 设置像素块颜色
setImg(ImgPath) 设置中心logo图标
setMargin(margin) 设置边缘宽度
setHeight(h)/setWidth(w) 设置宽高

6 Cpolar 内网穿透东西

cpolar极点云: 揭露一个本地Web站点至公网

效果:只需一行命令,就能够将内网站点发布至公网,便利给客户演示。高效调试微信大众号、小程序、对接支付宝网关等云端服务,进步您的编程效率。

  • 登录注册下载安装

download下载 – cpolar 极点云:www.cpolar.com/download

  • 获取cpolar账号Token

cpolar – secure introspectable tunnels to localhost:dashboard.cpolar.com/auth

  • 命令行运行cpolar,验证token
cpolar authtoken MGU1OWY4NWItXXXXXXXXXXXXXXXX
  • 完结简略内网穿透:cpolar 协议名 内网端口
cpolar http 8080

OAuth2.0 第三方授权登录(微信和Gitee)

留意:每次重启后随机域名会改动!

Forwarding里边的域名便是公网域名,能够给其他外网设备拜访。

7 OAuth2.0 – 微信登录

7.1 准备工作

扫码登陆微信有两种完结方法

  1. 依据微信大众渠道的扫码登录

    让第三方运用投入微信的怀有而规划的,这第三方运用指的是比方android、ios、网站、体系等;

  2. 依据微信敞开渠道的扫码登录(For common people) 为了让程序员小伙伴利用微信自家技能(大众号、小程序)开发大众号、小程序而准备的。

区别 微信敞开渠道需求开企业认证才能注册。 微信大众渠道需求认证微信服务号,才能进行扫码登录的开发。只需恳求一个大众号。

关于初学者,即没有企业认证,也纷歧定有自己的大众号,就只能运用测验大众号

测验大众号恳求地址:微信大众渠道 (qq.com)

mp.weixin.qq.com/debug/cgi-b…

1. 接口装备信息

接入概述 | 微信敞开文档 (qq.com)

参数 阐明
URL 此处要加协议,以及对应的检验接口
token 随意设置自定义token

示例:

URL : 63c27f47.r7.cpolar.top/wechat/chec…

Token :AASAdd

2. 网页授权获取用户基本信息 – 修改回调域名

在本网页里边找到 – 网页服务 – 网页账号 – 网页授权获取用户基本信息 -修改:授权回调页面域名

  • 回调域名效果

    用户在网页授权页赞同授权给大众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以保证安全可靠。沙盒号回调地址支撑域名和ip,正式大众号回调地址只支撑域名

  • 留意:此处填写域名,不是URL!无需https:// 等协议头

示例

回调地址:63c27f47.r7.cpolar.top

7.2 完结细节

网页授权 | 微信敞开文档 (qq.com)

基本步骤:

网页授权流程分为四步:

  1. 引导用户进入授权页面赞同授权,获取code
    1. 授权页面能够以二维码或许前端恳求跳转的形式给用户
    2. 授权链接参数解释:网页授权 | 微信敞开文档 (qq.com)
  2. 经过code交换网页授权access token(与根底支撑中的access_token不同)
  3. 如果需求,开发者能够改写网页授权access_token,避免过期
  4. 经过网页授权access token和openid获取用户基本信息(支撑UnionID机制)

7.2.0 微信接口校验

编写微信验证的测验接口办法:

@Controller
@RequestMapping("/wechat")
public class WeChatController {
    @GetMapping("/check")
    @ResponseBody
    public String WXCheck(@RequestParam("signature") String signature,
                          @RequestParam("timestamp") String timeStamp,
                          @RequestParam("nonce") String nonce,
                          @RequestParam("echostr") String echoStr){
        System.out.println("hello");
        System.out.println("echostr = " + echoStr);
        return echoStr;
    }
    ...
}

developers.weixin.qq.com/doc/offiacc…

完结 7.1 准备工作中的 URL,TOKEN,回调域名 的数据填写,装备后点击测验,显现 装备成功 则可进入下一步

示例

URL : 63c27f47.r7.cpolar.top/wechat/chec…

Token :AASAdd

回调地址:63c27f47.r7.cpolar.top

7.2.1 进入授权页

用户点击授权之后,服务商服务器建议一个带着授权码code的Get恳求,指向设置的回调地址(../wechat/auth)

@Controller
@RequestMapping("/wechat")
public class WeChatController {
	//登录二维码显现
    @GetMapping("/login")
    public void wxLogin(HttpServletResponse response) throws IOException {
        response.setContentType("image/png");
       QrCodeUtil.generate(WeChatUtil.getUrl(),400,400,"jpg",response.getOutputStream());
    }
	//回调接口
    @GetMapping("/auth")
    public Result callBack(String code, String state, HttpServletRequest request,
                           HttpServletResponse response, HttpSession session) throws IOException {
        WeChatToken weChatToken = WeChatUtil.getToken(code);
        System.out.println("token = " + weChatToken.getAccessToken() +"\n" +"getOpenId = " + weChatToken.getOpenid());
        return weChatToken.getErrCode() == 40029?
                new Result("fail", weChatToken.getErrMsg()):
                new Result("success", weChatToken);
    }
}

7.2.3 code换token

经过code交换token

//回调接口
@GetMapping("/auth")
public Result callBack(String code, String state, HttpServletRequest request,
                       HttpServletResponse response, HttpSession session) throws IOException {
    WeChatToken weChatToken = WeChatUtil.getToken(code);
    System.out.println("token = " + weChatToken.getAccessToken() +"\n" +"getOpenId = " + weChatToken.getOpenid());
    return weChatToken.getErrCode() == 40029?
        new Result("fail", weChatToken.getErrMsg()):
    	new Result("success", weChatToken);
}

7.2.4 token换用户数据

@GetMapping("/info")
    public Result getInfo(String token,String openId) throws IOException {
        WeChatUser user = WeChatUtil.getInfo(token, openId);
        System.out.println("user = " + user);
        return user.getErrCode()==40003?
                new Result("fail","获取失利"):
                new Result("success",user);
}

7.2.5 token改写

@GetMapping("/refresh")
public Result refresh(String refreshToken) throws IOException {
    WeChatToken weChatToken = WeChatUtil.refreshToken(refreshToken);
    return weChatToken.getErrCode()==40029?
            new Result("fail","获取失利"):
            new Result("success", weChatToken);
}

7.2.6 完整代码

Result
package com.example.domain;
import lombok.Data;
@Data
public class Result {
    private String status;//恳求状况
    private Object data;//数据
    private String msg;//信息
    public Result() {
    }
    public Result(String status, String msg) {
        this.status = status;
        this.msg = msg;
    }
    public Result(String status, Object data) {
        this.status = status;
        this.data = data;
    }
}
WeChatToken
package com.example.domain;
import lombok.Data;
@Data
public class WeChatToken {
    private String accessToken;//网页授权接口调用凭据,留意:此access_token与根底支撑的access_token不同
    private String expiresIn;//access_token接口调用凭据超时时间
    private String refreshToken;//用户改写access_token
    private String openid;//用户仅有标识
    private String scope;//用户授权的效果域
    private String isSnapshotUser;//是否为快照页形式虚拟账号
    private String unionId;//用户一致标识(针对一个微信敞开渠道帐号下的运用,同一用户的 unionid 是仅有的),只有当scope为"snsapi_userinfo"时回来
    private Integer errCode;//过错码
    private String errMsg;//过错信息
}
WeChatUser
package com.example.domain;
import lombok.Data;
@Data
public class WeChatUser {
    private String openid;//用户仅有id
    private String nickname;//微信昵称
    private Integer sex;//性别
    private String language;//语言
    private String city;//城市
    private String province;//省份
    private String country;//国家
    private String headImgUrl;//头像url
    private String[] privilege;//特权
    private String unionID;//用户综合id
    private Integer errCode;//过错码
    private String errMsg;//过错信息
}
WechatUtil

其间:RedirectUri是运用cpolar内网穿透形成的一个外网地址,微信无法验证localhost地址,请在微信敞开渠道和此次将域名和URL修改为自己的服务器指定地址或许内网穿透地址

public class WeChatUtil {
    //常量区
    public static final String AppId = "wxxx1234567";
    public static final String AppSecret = "1122344555566678";
    public static final String RedirectUri = "https://http://63c27f47.r7.cpolar.top//wechat/auth";63c27f47.r7.cpolar.top
    //RedirectUri是运用cpolar内网穿透形成的一个外网地址,微信无法验证本地地址,请在微信敞开渠道和此次将域名和URL修改为自己的服务器指定地址或许内网穿透地址
    //http恳求客户端
    static CloseableHttpClient httpClient = HttpClientBuilder.create().build();
    //办法区
    /**
     * Get encoded WeChat Authorization Url which embedded AppID and so on
     * @return completed url
     */
    public static String getUrl(){
        //url要转为 UrlEncode编码格局
        String CodedRedirectUri = URLEncoder.encode(RedirectUri, StandardCharsets.UTF_8);
        return "https://open.weixin.qq.com/connect/oauth2/authorize?" +
                "appid="+AppId+
                "&redirect_uri="+CodedRedirectUri+
                "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect&forcePopup=true";
    }
    /**
     * Get token object by code
     * @param code 授权码
     * @return 回来 WeChatToken 目标
     * @throws IOException IO反常
     */
    public static WeChatToken getToken(String code) throws IOException {
        String tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?"+
                "appid="+AppId+
                "&secret="+AppSecret+
                "&code="+code+
                "&grant_type=authorization_code";
        //get恳求目标装入url
        HttpGet httpGet = new HttpGet(tokenUrl);
        String responseResult = "";
        //建议恳求 如果恳求成功 则接收回来的数据转为UTF-8格局
        HttpResponse response = httpClient.execute(httpGet);
        if (response.getStatusLine().getStatusCode()==200) {
            responseResult = EntityUtils.toString(response.getEntity(),"UTF-8");
        }
        //将结果封装到WechatToken目标 并回来
        return JSON.parseObject(responseResult, WeChatToken.class);
    }
    /**
     * Refresh token
     * @param token:not accessToken,this is refresh token
     * @return new Token Object
     * @throws IOException IO反常
     */
    public static WeChatToken refreshToken(String token) throws IOException {
        String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?" +
                "appid="+AppId+
                "&grant_type=refresh_token" +
                "&refresh_token="+token;
        HttpGet httpGet =new HttpGet(url);
        //履行改写
        HttpResponse response = httpClient.execute(httpGet);
        String jsonStr = "";
        if (response.getStatusLine().getStatusCode()==200) {
            jsonStr = EntityUtils.toString(response.getEntity());
        }
        return JSON.parseObject(jsonStr, WeChatToken.class);
    }
    /**
     * Get user information by access_token and openID
     * @param accessToken 用户拜访token
     * @param openId 用户仅有id
     * @return 微信用户目标
     * @throws IOException IO反常
     */
    public static WeChatUser getInfo(String accessToken,String openId) throws IOException{
        String url = "https://api.weixin.qq.com/sns/userinfo?" +
                "access_token="+accessToken+
                "&openid=" +openId+
                "&lang=zh_CN";
        HttpGet httpGet = new HttpGet(url);
        String jsonStr = "";
        //履行恳求
        CloseableHttpResponse response = httpClient.execute(httpGet);
        if (response.getStatusLine().getStatusCode()==200) {
            jsonStr = EntityUtils.toString(response.getEntity());
        }
        System.out.println("jsonStr = " + jsonStr);
        return JSON.parseObject(jsonStr,WeChatUser.class);
    }
}
WeChatController
@RestController
@RequestMapping("/wechat")
public class WeChatController {
    /**
     * check the link status to WeChat Public Platform
     * @param signature signature
     * @param timeStamp timeStamp
     * @param nonce nonce
     * @param echoStr echoStr
     * @return the same echoStr
     */
    @GetMapping("/check")
    public String WXCheck(@RequestParam("signature") String signature,
                          @RequestParam("timestamp") String timeStamp,
                          @RequestParam("nonce") String nonce,
                          @RequestParam("echostr") String echoStr){
        return echoStr;
    }
    /**
     * Generate QRCode jumped to WeChat Authorization website
     * @param response 回来图片流
     * @throws IOException IO反常
     */
    @GetMapping("/login")
    public void wxLogin(HttpServletResponse response) throws IOException {
        response.setContentType("image/png");
        QrCodeUtil.generate(WeChatUtil.getUrl(),400,400,"jpg",response.getOutputStream());
    }
    /**
     * Authorize code to get token
     * @param code code
     * @param state state
     * @param request request
     * @param response response
     * @param session session
     * @return result
     * @throws IOException io
     */
    @GetMapping("/auth")
    public Result callBack(String code, String state, HttpServletRequest request,
                           HttpServletResponse response, HttpSession session) throws IOException {
        WeChatToken weChatToken = WeChatUtil.getToken(code);
        System.out.println("token = " + weChatToken.getAccessToken() +"\n" +"getOpenId = " + weChatToken.getOpenid());
        return weChatToken.getErrCode() == 40029?
                new Result("fail", weChatToken.getErrMsg()):
                new Result("success", weChatToken);
    }
    /**
     * Refresh token
     * @param refreshToken refreshToken
     * @return new token
     * @throws IOException io
     */
    @GetMapping("/refresh")
    public Result refresh(String refreshToken) throws IOException {
        WeChatToken weChatToken = WeChatUtil.refreshToken(refreshToken);
        return weChatToken.getErrCode()==40029?
                new Result("fail","获取失利"):
                new Result("success", weChatToken);
    }
    /**
     * Get user information
     * @param token token
     * @param openId openID
     * @return user info
     * @throws IOException io
     */
    @GetMapping("/info")
    public Result getInfo(String token,String openId) throws IOException {
        WeChatUser user = WeChatUtil.getInfo(token, openId);
        System.out.println("user = " + user);
        return user.getErrCode()==40003?
                new Result("fail","获取失利"):
                new Result("success",user);
    }
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>HELLO</title>
</head>
<body>
  <h1>LOGIN</h1>
  <a href="/wechat/login">微信登录</a>
</body>
</html>

8 OAuth2.0 – Gitee

8.1 准备工作

Gitee OAuth 文档

在Gitee官网登录后找到设置里边的第三方运用 创立第三方运用 – Gitee.com:

gitee.com/oauth/appli…

  • 保存 ClientIDClient Secret

  • 设置回调地址:Gitee和微信纷歧样,无需内网穿透,能够设置本地地址

    http://127.0.0.1:8080/gitee/auth
    
  • 主页地址效果不大,可随意填写

    http://127.0.0.1:8080/page/main.html
    
  • 权限:设置你的运用即将向用户讨取的权限

  • 其余参数请自定义

8.2 完结细节

和 微信的OAuth2.0授权大致相同

Result

@Data
public class Result {
    private String status;//恳求状况
    private Object data;//数据
    private String msg;//信息
    public Result() {
    }
    public Result(String status, String msg) {
        this.status = status;
        this.msg = msg;
    }
    public Result(String status, Object data) {
        this.status = status;
        this.data = data;
    }
}

GiteeUser

@Data
public class GiteeUser {
    private String id;//id
    private String name;//用户名
    private String email;//邮箱
    private String avatarUrl;//用户头像
    private String url;//json-数据
    private String htmlUrl;//json-用户主页
    private String starredUrl;//json-用户保藏
    private String blog;//用户博客衔接
    private String weibo;//绑定微博
    private String createdAt;//账号创立日期
    private Date updatedAt;//最近项目活跃时间
}

GiteeToken

@Data
public class GiteeToken {
    private String accessToken;//token
    private String tokenType;//token类型
    private String expiresIn;//token过期时长 86400s = 1day
    private String refreshToken;//改写token
    private String scope;//权限范围
    private String createdAt;//token创立时间戳
    private String error; //过错
    private String errorDescription;//过错信息
}

GiteeUtil

public class GiteeUtil {
    public static final String ClientID = "8e3exxxxxxxxxxxxxxxxxx";
    public static final String ClientSecret = "88efc2c187exxxxxxxx";
    public static final String RedirectUri = "http://127.0.0.1:8080/gitee/auth"; //回调地址
//    public static final String RedirectUri = "https://2dbfb5d2.r7.cpolar.top/gitee/auth"; //回调地址
    public static CloseableHttpClient httpClient = HttpClientBuilder.create().build();
    /**
     * 拼接 URL
     * @return url
     * @throws UnsupportedEncodingException Exception
     */
    public static String getUrl() throws UnsupportedEncodingException {
        String url = URLEncoder.encode(RedirectUri, StandardCharsets.UTF_8);
        return "https://gitee.com/oauth/authorize?client_id=" + ClientID + "&redirect_uri=" + url + "&response_type=code";
    }
    /**
     * 建议恳求获取Token
     * @param code 校验码
     * @return 回来数据
     * @throws Exception exception
     */
    public static GiteeToken getToken(String code) throws Exception {
        //新建httpClient目标 新建post恳求目标
        HttpPost postRequest = new HttpPost("https://gitee.com/oauth/token");
        //post恳求目标传入值
        StringEntity input = new StringEntity(
                        "grant_type=authorization_code&" +
                        "code=" + code +
                        "&client_id=" + ClientID +
                        "&redirect_uri=" + RedirectUri +
                        "&client_secret=" + ClientSecret);
        input.setContentType("application/x-www-form-urlencoded");
        postRequest.setEntity(input);
        //httpClient履行 post恳求 并获取回来内容
        HttpResponse response = httpClient.execute(postRequest);
        HttpEntity entity = response.getEntity();
        String jsonStr = EntityUtils.toString(entity);
        System.out.println("JSONSTR: "+jsonStr);
        //控制台
        GiteeToken gt = JSON.parseObject(jsonStr,GiteeToken.class);
        return gt;
    }
    /**
     * 改写 Gitee 的 token
     * @param refreshToken 从前获取到的refreshToken
     * @return 回来新的 GiteeToken 目标
     * @throws IOException exception
     */
    public static GiteeToken RefreshToken(String refreshToken) throws IOException {
        HttpPost postRequest = new HttpPost("https://gitee.com/oauth/token");
        StringEntity input = new StringEntity("grant_type=refresh_token&refresh_token=" + refreshToken);
        input.setContentType("application/x-www-form-urlencoded");
        //数据传入办法体
        postRequest.setEntity(input);
        //履行恳求
        HttpResponse response = httpClient.execute(postRequest);
        HttpEntity entity = response.getEntity();
        String jsonStr = EntityUtils.toString(entity);
        System.out.println(jsonStr);
        //将JSON数据实例化为GiteeToken目标
        return JSON.parseObject(jsonStr, GiteeToken.class);
    }
    /**
     * 依据用户的token获取用户的信息
     * @param token access_token
     * @return W
     * @throws IOException exception
     */
    public static GiteeUser getInfo(String token) throws IOException {
        //装备
        HttpGet emailGet = new HttpGet("https://gitee.com/api/v5/emails?access_token="+token);
        HttpGet userGet = new HttpGet("https://gitee.com/api/v5/user?access_token="+token);
        //履行恳求获取内容
        HttpEntity entity1 = httpClient.execute(emailGet).getEntity();
        HttpEntity entity2 = httpClient.execute(userGet).getEntity();
        //获取邮箱
        String jsonStr4email = EntityUtils.toString(entity1);
        String substring = jsonStr4email.substring(1, jsonStr4email.length()-1);
        String email = JSON.parseObject(substring).getString("email");
        //获取用户
        String jsonStr4User = EntityUtils.toString(entity2);
        GiteeUser giteeUser = JSON.parseObject(jsonStr4User, GiteeUser.class);
        giteeUser.setEmail(email);//将邮箱信息增加至用户
        return giteeUser;
    }
}

GiteeController

@Controller
@RequestMapping("/gitee")
public class GiteeController {
    /**
     * 拼接拜访地址
     * @return 跳转到拼接了clientID的url
     */
    @GetMapping("/login")
    public String giteeLogin() throws UnsupportedEncodingException {
        return "redirect:"+GiteeUtil.getUrl();
    }
    /**
     * Gitee 登录校验
     * @param code 授权校验码
     * @param session session
     * @return res
     * @throws Exception io
     */
    @GetMapping("/auth")
    @ResponseBody
    public Result giteeAuth(@RequestParam("code") String code, HttpSession session) throws Exception {
        GiteeToken giteeToken = GiteeUtil.getToken(code);
        String token = giteeToken.getAccessToken();
        System.out.println("giteeToken.toString() = " + giteeToken.toString());
        return giteeToken.getError() ==null ?
                new Result("success",giteeToken):
                new Result("fail",giteeToken.getErrorDescription());
    }
    /**
     * Refresh token
     * @param refreshToken 从前 GiteeToken的refreshToken
     * @return 新 GiteeToken目标
     * @throws IOException io
     */
    @GetMapping("refresh")
    @ResponseBody
    public Result refreshToken(String refreshToken) throws IOException {
        GiteeToken newGiteeToken = GiteeUtil.RefreshToken(refreshToken);
        //回来改写状况
        return newGiteeToken.getError()!=null?
                new Result("fail",newGiteeToken.getErrorDescription()):
                new Result("success",newGiteeToken);
    }
    /**
     * Get gitee user information
     * @param token token
     * @return res
     * @throws IOException io
     */
    @GetMapping("/info")
    @ResponseBody
    public Result getInfo(String token) throws IOException {
        return new Result("success",GiteeUtil.getInfo(token));
    }
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>HELLO</title>
</head>
<body>
  <h1>LOGIN</h1>
  <a href="/gitee/login">Gitee登录</a>
</body>
</html>