最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 实现Web端第三方登录

    正文概述 掘金(神奇的程序员)   2021-04-08   572

    前言

    前一阵子,为我的开源项目添加了第三方登录的功能,实现过程还算顺利,本文就跟大家分享下我的实现思路与过程,欢迎各位感兴趣的开发者阅读本文。

    环境搭建

    我的项目后端基于SpringBoot搭建,所以此处直接采用justauth库来做第三方登录。

    引入依赖

    pom.xml中添加下属代码。

    <!--第三方登录库-->
    <dependency>
        <groupId>me.zhyd.oauth</groupId>
        <artifactId>JustAuth</artifactId>
        <version>1.15.9</version>
    </dependency>
    <!--http请求库-->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.8.0</version>
    </dependency>
    

    封装工具类

    根据文档所述,我们封装几个我们需要平台的工具类,代码如下所示:

    package com.lk.utils;
    
    import me.zhyd.oauth.config.AuthConfig;
    import me.zhyd.oauth.request.*;
    
    import java.util.ArrayList;
    import java.util.List;
    
    // 第三方授权登录工具类
    public class AuthUtil {
        public static AuthRequest getGithubRequest() {
            return new AuthGithubRequest(AuthConfig.builder()
                    .clientId("平台id")
                    .clientSecret("平台密钥")
                    .redirectUri("回调地址")
                    .build());
        }
    
        public static AuthRequest getBaiduRequest() {
            return new AuthBaiduRequest(AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("")
                    .build());
        }
    
        public static AuthRequest getGiteeRequest() {
            return new AuthGiteeRequest(AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("")
                    .build());
        }
    
        // 开源中国授权
        public static AuthRequest getOschinaRequest() {
            return new AuthOschinaRequest(AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("")
                    .build());
        }
    
        // 腾讯云授权
        public static AuthRequest getCodingRequest() {
            List<String> scopeList = new ArrayList<>();
            scopeList.add("user");
            return new AuthCodingRequest(AuthConfig.builder()
                    .clientId("")
                    .clientSecret("")
                    .redirectUri("")
                    .scopes(scopeList)
                    .codingGroupName("")
                    .build());
        }
    }
    

    实现过程

    此处我们来看下具体的实现过程。

    后端实现

    此处我们需要写2个请求接口,供客户端调用。

    • 生成授权链接
    • 获取用户信息

    生成授权链接

    我们需要客户端传一个平台名称参数,随后我们根据平台名称来调用我们刚才封装好的工具类中的方法,实现代码如下所示:

        @ApiImplicitParams({
                @ApiImplicitParam(name = "platform", value = "平台名称", dataType = "String", paramType = "query", example = "GitHub", required = true)
        })
        @ApiOperation(value = "获取第三方登录授权地址", notes = "授权url地址")
        // 允许跨域访问
        @CrossOrigin()
        @RequestMapping(value = "/getAuthorize", method = RequestMethod.GET)
        public ResultVO<?> getAuthorize(@RequestParam(value = "platform") String platform) {
            AuthRequest authRequest = null;
            switch (platform) {
                case "github":
                    authRequest = AuthUtil.getGithubRequest();
                    break;
                case "gitee":
                    authRequest = AuthUtil.getGiteeRequest();
                    break;
                case "baidu":
                    authRequest = AuthUtil.getBaiduRequest();
                    break;
                case "oschina":
                    authRequest = AuthUtil.getOschinaRequest();
                    break;
                case "coding":
                    authRequest = AuthUtil.getCodingRequest();
                    break;
                default:
                    log.error("未识别的平台" + platform);
                    return ResultVOUtil.error(-1, "平台未识别,未找到处理方法。");
            }
            // 生成状态码
            String state = AuthStateUtils.createState();
            //  生成授权链接
            String authorizeUrl = authRequest.authorize(state);
            HashMap<String, String> result = new HashMap<>();
            result.put("authorizeUrl", authorizeUrl);
            // 将状态码给客户端,授权成功后获取用户信息时将state传回服务端,保证请求完整性,防止CSRF风险
            result.put("state", state);
            return ResultVOUtil.success(result);
        }
    
    

    获取用户信息

    客户端拿到授权链接后,用户同意授权,第三方网站返回code码,客户端携带授权链接接口返回的state码与code码,用来获取用户信息。

    实现代码如下:

        @ApiOperation(value = "第三方登录", notes = "用户授权后,通过第三方网站返回的字段来获取用户信息,随后执行登录操作")
        // 允许跨域访问
        @CrossOrigin()
        @RequestMapping(value = "/authorizeLogin", method = RequestMethod.POST)
        public ResultVO<?> authorizeLogin(@ApiParam(name = "传入授权成功后返回的信息", required = true) @Valid @RequestBody GitHubLoginDto loginDto) throws Exception {
            String state = loginDto.getState();
            String code = loginDto.getCode();
            String platform = loginDto.getPlatform();
            AuthRequest authRequest;
            // 用户信息
            AuthResponse<?> result;
            AuthCallback callback = new AuthCallback();
            callback.setState(state);
            callback.setCode(code);
            switch (platform) {
                case "github":
                    authRequest = AuthUtil.getGithubRequest();
                    result = authRequest.login(callback);
                    break;
                case "gitee":
                    authRequest = AuthUtil.getGiteeRequest();
                    result = authRequest.login(callback);
                    break;
                case "baidu":
                    authRequest = AuthUtil.getBaiduRequest();
                    result = authRequest.login(callback);
                    break;
                case "oschina":
                    authRequest = AuthUtil.getOschinaRequest();
                    result = authRequest.login(callback);
                    break;
                case "coding":
                    authRequest = AuthUtil.getCodingRequest();
                    result = authRequest.login(callback);
                    break;
                default:
                    log.error("未识别的平台" + platform);
                    return ResultVOUtil.error(-1, "平台未识别,用户信息获取失败");
            }
            if (result.getData() == null) {
                // 授权失败, 返回错误信息
                return ResultVOUtil.error(-1, "授权失败:" + result.getMsg());
            }
            JSONObject data = new JSONObject(result.getData());
            log.info(platform + "的用户信息: " + data);
            
            return LoginUtil.getLoginResult(data);
        }
    
    

    前端实现

    接下来我们来看下前端部分的具体实现。

    创建授权页面

    在后端实现章节中,获取授权链接时,配置了一个回调地址,这个地址里会自动拼接上第三方平台返回的code码,因此我们需要创建一个html页面,用于获取地址栏中的code码,将获取到的code放进本地存储中,方便我们在登录页面获取。

    实现代码如下所示:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>授权回调页面</title>
      <script type="text/javascript">
        /**
         * 获取url参数
         * @param url
         * @param variable
         */
        function getQueryVariable(
          url,
          variable
        ) {
          // 对url进行截取
          url = url.substring(url.indexOf("?"), url.length);
          const query = url.substring(1);
          const vars = query.split("&");
          for (let i = 0; i < vars.length; i++) {
            const pair = vars[i].split("=");
            if (pair[0] === variable) {
              return pair[1];
            }
          }
          return -1;
        }
        window.onload = () => {
          // 获取url中的授权码
          const code = getQueryVariable(window.location.href, "code");
          // 将授权码放进本地存储中
          if (code !== -1) {
            localStorage.setItem("authCode", code);
          }else {
            localStorage.setItem("authCode", "");
          }
          // 关闭页面
          window.close();
        }
      </script>
    </head>
    <body>
    
    </body>
    </html>
    

    打开授权页面

    接下来,我们需要在登录页面打开后端接口所返回的授权页,用户授权成功后,将code与state回传给服务端,从而实现登录。

    那么,我们接下来要考虑的是在当前页面以弹窗形式打开授权页面后,用户授权成功后,如何立即获取到第三方平台返回的code调用授权登录接口。

    经过一番思考后,我有了下述思路:

    • 使用window.open()打开授权窗口
    • 在登录页面监听localstorage改变
    • 授权成功,授权页面向localstorage写入code
    • 登录页面监听到改变,根据code码判断是用户是否授权成功
    • 授权成功则执行登录操作

    实现代码如下:

    <!--登录页面-->
    <template>
        <!--第三方登录-->
        <div
          class="auth-panel"
          v-if="isLoginStatus === loginStatusEnum.NOT_LOGGED_IN"
        >
          <div class="item-panel" @click.once="getAuthorize('github')">
            <img src="@/assets/img/auth/github.png"  />
          </div>
          <div class="item-panel" @click.once="getAuthorize('gitee')">
            <img src="@/assets/img/auth/gitee.png"  />
          </div>
          <div class="item-panel" @click.once="getAuthorize('baidu')">
            <img src="@/assets/img/auth/baidu.png"  />
          </div>
          <div class="item-panel" @click.once="getAuthorize('oschina')">
            <img src="@/assets/img/auth/oschina.png"  />
          </div>
          <div class="item-panel" @click.once="getAuthorize('coding')">
            <img src="@/assets/img/auth/coding.png"  />
          </div>
        </div>
    </template>
    
    <script lang="ts">
      name: "login",
      data(){
          state: "",
          platform: ""  
      },  
      methods: {
        getAuthorize: function(name: string) {
          // 获取授权链接
          this.$api.authLoginAPI
            .getAuthorize({ platform: name })
            .then((res: responseDataType<getAuthorizeDataType>) => {
              if (!res.data.state || !res.data.authorizeUrl)
                throw "服务器错误: 授权链接获取失败";
              const authorizeUrlres = res.data.authorizeUrl;
              // 更新状态码与登录平台名称
              this.state = res.data.state;
              this.platform = name;
              // 打开授权窗口
              window.open(
                authorizeUrlres,
                "_blank",
                "toolbar=no,width=800, height=600"
              );
              // 开始监听localStorage,获取授权码
              window.addEventListener("storage", this.getAuthCode);
            });
        },
        getAuthCode: function() {
          // 获取授权码
          const code = localStorage.getItem("authCode");
          localStorage.removeItem("authCode");
          // 移除localStorage监听
          this.removeStorageListener();
          if (code) {
            // 调用登录函数
            this.authLogin(this.state, code, this.platform);
            return;
          }
          throw this.platform + "授权码获取失败";
        },
        authLogin: function(state: string, code: string, platform: string) {
          this.$api.authLoginAPI
            .authorizeLogin({
              state: state,
              code: code,
              platform: platform
            })
            .then((res: responseDataType) => {
              if (res.code == 0) {
                // 存储当前用户信息
                
                return;
              }
              // 切回登录界面
             
            });
        }
      }
    </script>
    

    实现效果

    接下来我们来看下,最终实现的效果。

    实现Web端第三方登录

    项目地址

    • 在线体验地址:chat-system
    • GitHub地址:chat-system-github

    写在最后

    • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注?
    • 本文首发于掘金,未经许可禁止转载?

    起源地下载网 » 实现Web端第三方登录

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元