JustAuth,如你所见,它仅仅是一个第三方授权登录工具类库,它可以让我们脱离繁琐的第三方登录SDK,让登录变得So easy!

github地址:https://github.com/justauth/JustAuth

对于spirng boot+Vue前后端分离的项目实现第三方登录比单纯spring boot相对比较麻烦,所以在此做个记录。

1. 环境准备

1.1. 公网服务器准备

首先准备一台有公网 IP 的服务器,可以选用阿里云或者腾讯云,百度云也行,好像百度云最近有优惠。

1.2. 内网穿透 frp 搭建

frp 安装程序:https://github.com/fatedier/frp/releases

1.2.1. frp 服务端搭建

服务端搭建在上一步准备的公网服务器上,因为服务器是 centos7 x64 的系统,因此,这里下载安装包版本为 linux_amd64 的 frp_0.29.1_linux_amd64.tar.gz 。

       用shell远程连接服务器,命令行输入

  1. 下载安装包(下载速度可能会比较慢)

    $ wget https://github.com/fatedier/frp/releases/download/v0.29.1/frp_0.29.1_linux_amd64.tar.gz
  2. 解压安装包
    $ tar -zxvf frp_0.29.1_linux_amd64.tar.gz

     

  3. 修改配置
    $ cd frp_0.29.1_linux_amd64
    $ vim frps.ini
    
    [common]                                                                                                                  
    bind_port = 7100                                                                                                          
    vhost_http_port = 7200

     

  4. 启动 frp 服务端   注意:/root/frp_0.29.1_linux_amd64为frps安装的绝对路径
    $nohup /root/frp_0.29.1_linux_amd64/frps -c  /root/frp_0.29.1_linux_amd64/frps.ini
    
    nohup: ignoring input and appending output to ‘nohup.out’
    

 运行后frps的运行日志将会在‘nohup.out’文件中输出

1.2.2. frp 客户端搭建

客户端搭建在本地的 Windows 上,因此下载安装包版本为 windows_amd64 的 frp_0.29.1_windows_amd64.zip 。

1.直接下载压缩安装包

https://github.com/fatedier/frp/releases/download/v0.29.1/frp_0.29.1_windows_amd64.zip

2.解压文件到自己的电脑中

3.修改配置文件frpc.ini

[common]
server_addr = 119.29.528.35
server_port = 7000


[web]
type = http
local_port = 8080
custom_domains = www.example.cn

server_addr = 你外网的服务器

Iplocal_port = 你本地spring boot运行端口
custom_domains 你外网服务器IP进行dns绑定的域名

4.启动frpc客户端(双击直接运行我好像运行不起来)使用CMD执行以下命令

//切换到解压文件目录
c:> cd frp_0.29.1_windows_amd64
//运行
c:> frpc.exe
2019/11/21 10:20:35 [I] [service.go:249] [01a75fadd6a3998d] login to server success, get run id [01a75fadd6a3998d], server udp port [0]
2019/11/21 10:20:35 [I] [proxy_manager.go:144] [01a75fadd6a3998d] proxy added: [web]
2019/11/21 10:20:35 [I] [control.go:164] [01a75fadd6a3998d] [web] start proxy success

:现在当我们在浏览器输入 http://www.eample.com :7200的时候,网络流量其实会经历以下几个步骤:

  1. 通过之前配的 DNS 域名解析会访问到我们的公网服务器 119.29.528.35 的 7200端口
  2. 再经过 frp 穿透到我们的 windows电脑的 8080 端口
  3. 此时 8080 就是我们的应用程序端口

如果需要去掉7200端口可以自己配置nginx进行代理到本地的 7200 端口,需要的话自己百度

 1.3. 第三方平台申请

  1. 前往 https://connect.qq.com/

  2. 申请开发者

  3. 应用管理 -> 添加网站应用,等待审核通过即可

其他平台相类似;

2. 主要代码 

思路:用户点击前端Vue第三方按钮,后端/render/{source}接口授权接收请求返回重定向的第三方登录地址,前端Vue接收到后可直接跳转到授权界面或者通过弹窗的方式打开第三方登录界面也行,登录成功后第三方返回回调地址到我们外网的域名,在经过我们的内网映射到本地到/callback/{source}接口,我们获取到token后进行数据库数据存储,并用Token去获取openid和其他信息,我们创建uuid产生Key用redis进行保存用户信息,并把key携带到地址中重定向到首页,首页解析地址是否包含我们携带的key有的话就可以到后台redis中请求到用户的信息了,返回前端后提示登录成功并跳转到用户中心界面。

2.1. pom.xml

<!--第三方授权登录的工具类JustAuth库-->
<dependency>
   <groupId>me.zhyd.oauth</groupId>
   <artifactId>JustAuth</artifactId>
   <version>1.13.1</version>
</dependency>
<!-- Lombok -->
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
</dependency>
<!-- Redis -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- mybatis-plus -->
 <dependency>
       <groupId>com.baomidou</groupId>
       <artifactId>mybatis-plus-boot-starter</artifactId>
       <version>${mybatis-plus.version}</version>
   </dependency>

<!-- druid -->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>${druid.version}</version>
</dependency>

2.2 application.yml配置

server:
  port: 8080
  servlet:
    context-path: /oauth

spring:
  redis:
    host: localhost
    # 连接超时时间(记得添加单位,Duration)
    timeout: 10000ms
    # Redis默认情况下有16个分片,这里配置具体使用的分片
    # database: 0
    lettuce:
      pool:
        # 连接池最大连接数(使用负值表示没有限制) 默认 8
        max-active: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-wait: -1ms
        # 连接池中的最大空闲连接 默认 8
        max-idle: 8
        # 连接池中的最小空闲连接 默认 0
        min-idle: 0
  cache:
    # 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配
    type: redis
datasource:
        master:
          url: jdbc:mysql://127.0.0.1:3306/oauth?characterEncoding=UTF-8&useUnicode=true&useSSL=false
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver

2.3 创建数据库表oauth并创建表结构

-- ----------------------------
-- Table structure for sys_user_social
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_social`;
CREATE TABLE `sys_user_social` (
  `userId` varchar(255) NOT NULL COMMENT '用户Id',
  `providerId` varchar(255) NOT NULL COMMENT '平台类型qq,wechat,weibo等',
  `providerUserId` varchar(255) NOT NULL COMMENT '社交平台唯一ID',
  `rank` int(11) NOT NULL DEFAULT '0' COMMENT '等级',
  `displayName` varchar(255) DEFAULT NULL COMMENT '昵称',
  `profileUrl` varchar(512) DEFAULT NULL COMMENT '配置文件地址',
  `imageUrl` varchar(512) DEFAULT NULL COMMENT '头像地址',
  `accessToken` varchar(512) NOT NULL COMMENT 'token',
  `secret` varchar(512) DEFAULT NULL COMMENT '秘钥',
  `refreshToken` varchar(512) DEFAULT NULL COMMENT '刷新token',
  `expireTime` bigint(20) DEFAULT NULL COMMENT '过期时间',
  `create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`userId`,`providerId`,`providerUserId`),
  UNIQUE KEY `UserConnectionRank` (`userId`,`providerId`,`rank`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社交登录表';

2.4 写控制类

/**
 * @author 淋汾
 * @version 1.0
 * @description 第三方授权登录
 * @date 2019/11/19 9:28
 */
@RestController
@RequestMapping("/oauth")
public class LoginOauthController {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ISysUserSocialService userSocialService;
    @Autowired
    private ISysDepartService sysDepartService;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysBaseAPI sysBaseAPI;
    
    //第三方登录回调地址
    private String callBackBaseUrl = "http://www.example.cn:7200/oauth/oauth/callback";
    //本地授权成功重定向地址
    private String url = "http://127.0.0.1:8081";

    /**
     * 授权请求地址
     *
     * @param source
     * @param response
     * @return
     * @throws IOException
     */
    @RequestMapping("/render/{source}")
    public Result<String> renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
        System.out.println("进入render:" + source);
        AuthRequest authRequest = getAuthRequest(source);
        String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
        System.out.println(authorizeUrl);
        Result<String> result = new Result<String>();
        result.setSuccess(true);
        result.setResult(authorizeUrl);
        result.setMessage("正在进行跳转");
//        response.sendRedirect(authorizeUrl);
        return result;
    }

    /**
     * oauth平台中配置的授权回调地址,以本项目为例,在创建github授权应用时的回调地址应为:http://127.0.0.1:8080/oauth/callback/github
     */
    @RequestMapping("/callback/{source}")
    public Object login(@PathVariable("source") String source, AuthCallback callback, HttpServletResponse response) throws IOException {
        String uuid = "";
        Result<JSONObject> result = new Result<JSONObject>();
        System.out.println("进入callback:" + source + " callback params:" +
                JSONObject.toJSONString(callback));
        AuthRequest authRequest = getAuthRequest(source);
        AuthResponse<AuthUser> authResponse = authRequest.login(callback);
        System.out.println(JSONObject.toJSONString(authResponse));

          /*  switch (source) {
                case "weibo":
                    authResponse.getData().getToken().getOpenId();
                    break;
            }
*/

        //通过getProviderid和getProviderUserId查询用户
        if (authResponse == null) {
            result.error500("授权失败");
            return result;
        }
        SysUserSocial sysUserSocial = userSocialService.lambdaQuery().eq(SysUserSocial::getProviderid, source)
                .eq(SysUserSocial::getProvideruserid, authResponse.getData().getToken().getOpenId()).one();
        if (sysUserSocial == null) {
            AuthToken token = authResponse.getData().getToken();
            sysUserSocial = new SysUserSocial();
            sysUserSocial.setProviderid(source);
            sysUserSocial.setProvideruserid(token.getOpenId());
            sysUserSocial.setAccesstoken(token.getAccessToken());
            sysUserSocial.setRefreshtoken(token.getRefreshToken());
            sysUserSocial.setExpiretime(token.getExpireIn());
            sysUserSocial.setDisplayname(authResponse.getData().getUsername());
            sysUserSocial.setProfileurl(authResponse.getData().getBlog());
            sysUserSocial.setImageurl(authResponse.getData().getAvatar());
            sysUserSocial.setSecret(token.getOauthTokenSecret());

            //TODO 社交账户未绑定用户数据库,进行绑定
             uuid = UUID.randomUUID().toString();
            redisUtil.set(uuid, sysUserSocial, 1000 * 60 * 10);
            System.out.println("存数据的key"+uuid);
            result.error500("社交账户未绑定用户");
            response.sendRedirect(url + "/user/social-bind?key=" + uuid);
            return result;
        }

        //通过用户ID超找用户,并进行登录
        SysUser user = sysUserService.getById(sysUserSocial.getUserid());
        if (user == null) {
            result.error500("未找到用户,请绑定用户");
            return result;
        }
        //用户登录信息
        userInfo(user, result);
        sysBaseAPI.addLog("用户名: " + user.getUsername() + ",第三方" + source + "登录成功!", CommonConstant.LOG_TYPE_1, null);
         uuid = UUID.randomUUID().toString();
        redisUtil.set(uuid, result.getResult(), 1000 * 60 * 10);
        response.sendRedirect(url + "/user/login?key=" + uuid);
        return result;
    }

    @PostMapping("/user_info")
    public Result<JSONObject>  getSocialUserInfo(@RequestBody String key) {
        Result<JSONObject> result = new Result<JSONObject>();
        System.out.println("获取数据的key"+key);
        if (key == null || key.equals("")) {
            return result.error500("key不能为空");
        }
        key = JSON.parseObject(key).getString("key");
        //从Redis读取数据
        Object data = redisUtil.get(key);
        //删除Redis中的数据
         redisUtil.del(key);
        if (data == null) {
            return result.error500("数据请求失败");
        }
        result.setResult((JSONObject) data);
        result.success("登录成功");
        return result;
    }

    /**
     * 社交登录绑定
     * @param user 登录信息
     * @return
     */
    @PostMapping("/bind")

    public Result register(@RequestBody SocialBindVO user) {
        return sysUserService.doPostSignUp(user);
    }


    /**
     * 取消授权登录
     *
     * @param source 平台
     * @param token  token
     * @return
     * @throws IOException
     */
    @RequestMapping("/revoke/{source}/{token}")
    public Object revokeAuth(@PathVariable("source") String source, @PathVariable("token") String token) throws IOException {
        Result result = userSocialService.unBind(source, token);
        AuthRequest authRequest = getAuthRequest(source);
        try {
            authRequest.revoke(AuthToken.builder().accessToken(token).build());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 刷新Token
     *
     * @param source
     * @param token
     * @return
     */
    @RequestMapping("/refresh/{source}")
    public Object refreshAuth(@PathVariable("source") String source, String token) {
        AuthRequest authRequest = getAuthRequest(source);
        return   authRequest.refresh(AuthToken.builder().refreshToken(token).build());
    }

    /**
     * 用户信息
     *
     * @param sysUser
     * @param result
     * @return
     */
    private Result<JSONObject> userInfo(SysUser sysUser, Result<JSONObject> result) {

        String syspassword = sysUser.getPassword();
        String username = sysUser.getUsername();
        // 生成token
        String token = JwtUtil.sign(username, syspassword);
        // 设置token缓存有效时间
        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
        redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);

        // 获取用户部门信息
        JSONObject obj = new JSONObject();
        List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
        obj.put("departs", departs);
        if (departs == null || departs.size() == 0) {
            obj.put("multi_depart", 0);
        } else if (departs.size() == 1) {
            sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
            obj.put("multi_depart", 1);
        } else {
            obj.put("multi_depart", 2);
        }
        obj.put("token", token);
        obj.put("userInfo", sysUser);
        result.setResult(obj);
        result.success("登录成功");
        return result;
    }

    /**
     * 根据具体的授权来源,获取授权请求工具类
     *
     * @param source
     * @return
     */
    private AuthRequest getAuthRequest(String source) {
        AuthRequest authRequest = null;
        switch (source) {
            case "dingtalk":
                authRequest = new AuthDingTalkRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/dingtalk")
                        .build());
                break;
            case "baidu":
                authRequest = new AuthBaiduRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/baidu")
                        .build());
                break;
            case "github":
                authRequest = new AuthGithubRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/github")
                        .build());
                break;
            case "gitee":
                authRequest = new AuthGiteeRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/gitee")
                        .build());
                break;
            case "weibo":
                authRequest = new AuthWeiboRequest(AuthConfig.builder()
                        .clientId("微博的APP ID")
                        .clientSecret("微博的APP Key")
                        .redirectUri(callBackBaseUrl + "/weibo")
                        .build());
                break;
            case "coding":
                authRequest = new AuthCodingRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/coding")
                        .build());
                break;
            case "tencentCloud":
                authRequest = new AuthTencentCloudRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/tencentCloud")
                        .build());
                break;
            case "oschina":
                authRequest = new AuthOschinaRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/oschina")
                        .build());
                break;
            case "alipay":
                // 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1,所以这儿的回调地址使用的局域网内的ip
                authRequest = new AuthAlipayRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .alipayPublicKey("")
                        .redirectUri(callBackBaseUrl + "/alipay")
                        .build());
                break;
            case "qq":
                authRequest = new AuthQqRequest(AuthConfig.builder()
                        .clientId("填写QQ的APP ID")
                        .clientSecret("填写QQ的APP Key")
                        .redirectUri(callBackBaseUrl + "/qq")
                        .build());
                break;
            case "wechat":
                authRequest = new AuthWeChatRequest(AuthConfig.builder()
                        .clientId("123")
                        .clientSecret("123")
                        .redirectUri(callBackBaseUrl + "/wechat")
                        .build());
                break;
            case "csdn":
                authRequest = new AuthCsdnRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/csdn")
                        .build());
                break;
            case "taobao":
                authRequest = new AuthTaobaoRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/taobao")
                        .build());
                break;
            case "google":
                authRequest = new AuthGoogleRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/google")
                        .build());
                break;
            case "facebook":
                authRequest = new AuthFacebookRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/facebook")
                        .build());
                break;
            case "douyin":
                authRequest = new AuthDouyinRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/douyin")
                        .build());
                break;
            case "linkedin":
                authRequest = new AuthLinkedinRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/linkedin")
                        .build());
                break;
            case "microsoft":
                authRequest = new AuthMicrosoftRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/microsoft")
                        .build());
                break;
            case "mi":
                authRequest = new AuthMiRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/mi")
                        .build());
                break;
            case "toutiao":
                authRequest = new AuthToutiaoRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/toutiao")
                        .build());
                break;
            case "teambition":
                authRequest = new AuthTeambitionRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/teambition")
                        .build());
                break;
            case "pinterest":
                authRequest = new AuthPinterestRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/pinterest")
                        .build());
                break;
            case "renren":
                authRequest = new AuthRenrenRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/teambition")
                        .build());
                break;
            case "stackoverflow":
                authRequest = new AuthStackOverflowRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/login_success")
                        .stackOverflowKey("")
                        .build());
                break;
            case "huawei":
                authRequest = new AuthHuaweiRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/huawei")
                        .build());
                break;
            case "wechatEnterprise":
                authRequest = new AuthHuaweiRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/wechatEnterprise")
                        .agentId("")
                        .build());
                break;
            case "kujiale":
                authRequest = new AuthKujialeRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/kujiale")
                        .build());
                break;
            case "gitlab":
                authRequest = new AuthGitlabRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/gitlab")
                        .build());
                break;
            case "meituan":
                authRequest = new AuthMeituanRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/meituan")
                        .build());
                break;
            case "eleme":
                authRequest = new AuthElemeRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/eleme")
                        .build());
                break;

            case "twitter":
                authRequest = new AuthTwitterRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri(callBackBaseUrl + "/twitter")
                        .build());
                break;
            default:
                break;
        }
        if (null == authRequest) {
            throw new AuthException("未获取到有效的Auth配置");
        }
        return authRequest;
    }
}

3 前端Vue

3.2 Vue登录界面

<template>
  <div class="main">
    <a-form :form="form" class="user-layout-login" ref="formLogin" id="formLogin">
     

       <div class="user-login-other">
        <span>其他登陆方式</span>
        <a><a-icon class="item-icon" @click="showOauthModal('qq')" type="qq"></a-icon></a>
        <a><a-icon class="item-icon" @click="showOauthModal('wechat')"  type="wechat"></a-icon></a>
        <a><a-icon class="item-icon" @click="showOauthModal('weibo')"  type="weibo-circle"></a-icon></a>
       <!-- <router-link class="register" :to="{ name: 'register' }">
          注册账户
        </router-link>-->
      </div>
    </a-form>


    <a-modal
      title="第三方授权登录"
      :visible="aouthVisible"
      :confirmLoading="aouthLoading"
      width="650px"
      :closable="false"
      :maskClosable="false">

      <template slot="footer">
        <a-button type="danger" @click="handleAaouthCancel">取消授权</a-button>
      </template>
      <iframe  :id="aouthId" :src="aouthUrl" frameborder="0" width="100%" height="350px" scrolling="auto"></iframe>
    </a-modal>
  </div>
</template>

<script>
  import api from '@/api'
  
  import Vue from 'vue'
  import { ACCESS_TOKEN ,ENCRYPTED_STRING} from "@/store/mutation-types"
  import JGraphicCode from '@/components/jeecg/JGraphicCode'
  import { postAction } from '@/api/manage'
  import { getAction } from '@/api/manage'
  import store from '@/store/'
  import { USER_INFO } from "@/store/mutation-types"
  import { message } from 'ant-design-vue';
  export default {,
    data () {
          aouthVisible: false,
          aouthLoading: false,
          aouthUrl: "/oauth/render/qq",
          aouthId: "https://www.baidu.com",
      }
    },
    created () {
        const key = getUrlKey('key');
        if (key != null && key !== '') {
            this.socialLogin(key);
            console.log("请求数据" + key);
        } else {
            console.log("没有请求数据")
        }
      Vue.ls.remove(ACCESS_TOKEN)


    },
    methods: {
      ...mapActions([ "SocialLogin" ]),
      
        showOauthModal(type) {
            let obj = {};
            //请求第三方授权登录
            postAction('/oauth/render/'+type, obj).then(res => {
                if (res.success) {
                    if (!res.result.token) {
                        // this.aouthUrl = res.result;
                        // this.aouthVisible = true;
                        window.location.href = res.result;
                        console.log(type + "请求第三方登录:" + JSON.stringify(res));
                    }

                } else {
                    message.error('打开第三方登录失败');
                }
            });
        },
        handleAaouthCancel(e) {
            console.log('Clicked cancel button');
            this.aouthVisible = false;
        },
        socialLogin(key) {
            let that=this;
            const socialBind = {
                key: key
            };

            that.SocialLogin(socialBind).then((res) => {
                this.departConfirm(res)
            }).catch((err) => {
                that.requestFailed(err);
            });

/*            postAction("/oauth/user_info", socialBind).then((res) => {
                console.log('提交的信息',JSON.stringify(res))
                if (!res.success) {
                    this.requestFailed(res.message)
                } else {
                    const userInfo = res.result.userInfo;
                    Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000);
                    store.commit('SET_INFO', userInfo);
                    Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)
                    this.$router.push({ name: "dashboard" })
                    this.departConfirm(res);
                }
            })*/
        }
    },
 function getUrlKey(name) {
    //解析URL中的key
  try {
    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.href) || [''])[1].replace(/\+/g, '%20')) || null;
  }catch (e) {
    return null;
  }
}

  }
</script>

<style lang="scss" scoped>

  .user-layout-login {
    label {
      font-size: 14px;
    }

    .getCaptcha {
      display: block;
      width: 100%;
      height: 40px;
    }

    .forge-password {
      font-size: 14px;
    }

    button.login-button {
      padding: 0 15px;
      font-size: 16px;
      height: 40px;
      width: 100%;
    }

    .user-login-other {
      text-align: left;
      margin-top: 24px;
      line-height: 22px;

      .item-icon {
        font-size: 24px;
        color: rgba(0,0,0,.2);
        margin-left: 16px;
        vertical-align: middle;
        cursor: pointer;
        transition: color .3s;

        &:hover {
          color: #1890ff;
        }
      }

      .register {
        float: right;
      }
    }
  }

</style>
<style>
  .valid-error .ant-select-selection__placeholder{
    color: #f5222d;
  }
</style>

3.2 Vue绑定界面

<template>
  <div class="main user-layout-register">
    <h3><span>第三方绑定账户</span></h3>
    <a-form ref="formRegister" :autoFormCreate="(form)=>{this.form = form}" id="formRegister">
      <a-form-item
        fieldDecoratorId="username">
        <a-input size="large" type="text" autocomplete="false" placeholder="请输入用户名"></a-input>
      </a-form-item>


      <a-form-item
        fieldDecoratorId="password">
        <a-input size="large" type="password" autocomplete="false" placeholder="确认密码"></a-input>
      </a-form-item>


      <a-form-item>
        <a-button
          size="large"
          type="primary"
          htmlType="submit"
          class="register-button"
          :loading="registerBtn"
          @click.stop.prevent="handleSubmit"
          :disabled="registerBtn">绑定账户
        </a-button>
        <router-link class="login" :to="{ name: 'login' }">返回登录</router-link>
        <router-link class="login" :to="{ name: 'register' }" style="float: right;margin-right: 10px">注册账户</router-link>
      </a-form-item>

    </a-form>
  </div>
</template>

<script>
    import {mixinDevice} from '@/utils/mixin.js'
    import {getUrlKey} from '@/components/_util/StringUtil.js'
    import {getAction, postAction} from '@/api/manage'



    export default {
        name: "SocialBind",
        components: {},
        mixins: [mixinDevice],
        data() {
            return {
                form: {
                    username: '',
                    password: '',
                    key: ''
                },
                registerBtn: false
            }
        },
        methods: {
            handleSubmit(){
                const _this = this
                const key = getUrlKey('key');
                console.log("地址中的key:"+key)
                this.form.validateFields((err, values) => {
                    if (!err) {
                        const socialBind = {
                            username:values.username,
                            password:values.password,
                            key: key
                        };
                        console.log('提交的信息',JSON.stringify(socialBind))
                        postAction("/oauth/bind", socialBind).then((res) => {
                            if (!res.success) {
                                this.bindFailed(res.message)
                            } else {
                                this.$notification.open({
                                    message: '成功',
                                    description:
                                        '社交账号绑定成功,2秒后返回登录页面',
                                    duration: 2,
                                });
                                setTimeout(() => {
                                    this.$router.push({name: 'login', params: {...values}})
                                }, 2000)


                            }
                        })
                    }
                });

            },bindFailed(message) {
                this.$notification['error']({
                    message: "绑定失败",
                    description: message,
                    duration: 2,
                });

            },
        }
    }
</script>
<style lang="scss">
  .user-register {

    &.error {
      color: #ff0000;
    }

    &.warning {
      color: #ff7e05;
    }

    &.success {
      color: #52c41a;
    }

  }

  .user-layout-register {
    .ant-input-group-addon:first-child {
      background-color: #fff;
    }
  }
</style>
<style lang="scss" scoped>
  .user-layout-register {

    & > h3 {
      font-size: 16px;
      margin-bottom: 20px;
    }

    .getCaptcha {
      display: block;
      width: 100%;
      height: 40px;
    }

    .register-button {
      width: 100%;
    }

    .login {
      float: right;
      line-height: 40px;
    }
  }
</style>

参考

1.Spring Boot 快速集成第三方登录功能:https://xkcoding.com/2019/05/22/spring-boot-login-with-oauth.html

2.JustAuth GitHub地址:https://github.com/justauth/JustAuth

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐