Auth2.0加上免费的QQ登录

语言: CN / TW / HK

我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛

Auth2.0+QQ三方登录

QQ互联申请

申请开发者

这个一般不乱填都会通过的,一般就是四天左右吧。

应用管理—创建应用

这里我申请了很多次才通过,发现了技巧就好了,我们可以不用先管后端怎么写,先把回调申请下来再去写也可以。

  1. 首先要把QQ登录图片(官方文档里有)放在登录下面

image.png

  1. 将这个图片设置一个超链接https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=(自己appid)

  2. 将这个前端放在自己的服务器上,最好是80端口

  3. 下面就是填写申请表了,要想好一个回调地址这个需要和后台对应起来的非常重要,这个申请很快一般就是当天下午或者第二天下午就有结果

这上面基本百度都有教程的,但是下面代码部分我就很奇怪怎么没有人写过码

业务代码

我这是SpringSecurity项目上加的QQ三方登录,其实都一样的。

数据库设计

不需要修改原有的用户表,我们写一个专门存放第三方登录存储表

image.png

用户表和三方登录表一对一关系,把外键放哪里表都可以

代码设计 开发文档接口介绍
  1. yml配置文件写好自己的应用内容

yml qq: oauth: appid: ******** appkey: ******* url: ********* #回调地址

  1. 代码逻辑

生成专属于自己的QQ登录链接防止防止CSRF攻击,用户点击我们生成的链接授权登录后,就直接访问我们的回调地址

java /** * QQ互联中提供的 appid 和 appkey 和 回调url */ @Value("${qq.oauth.appid}") public String APPID; @Value("${qq.oauth.appkey}") public String APPKEY; @Value("${qq.oauth.url}") public String URL; /** * 请求授权 */ @PostMapping(value = "/authqq") @ApiOperation("获得自己专属的qq登录路径") public AjaxResult qqAuth(HttpServletRequest request) { // 用于第三方应用防止CSRF攻击 String uuid = UUID.randomUUID().toString().replaceAll("-", ""); request.getServletContext().setAttribute("state",uuid); //获取Authorization Code String url = "https://graph.qq.com/oauth2.0/authorize?response_type=code" + "&client_id=" + APPID + "&redirect_uri=" +URL + "&state=" + uuid; return AjaxResult.success(url); }

回调接口,这里逻辑都写在了controller不推荐哟,记得自己改一下,这里逻辑

1.判断状态码state是否是咱们自己的

2.通过Authorization_Code获取Access_Token

3.获取回调后的openID这个id是用户对你系统唯一的id

4.通过Access_Token和openID获取QQ用户信息

逻辑部分

1.从数据库查openId是否存在(QQ用户是否第一次登录),第一次就返回前端让用户绑定当前系统账户,或者申请一个账号(这里有多种实现方式,比如我的是管理员分配账户没有注册接口或者直接用qq登录等操作)

2.不是第一次登录的话我们就直接用security登录方式登录

3.回忆一下security登录userDetailsService.loadUserByUsername()获得UserDetails登录用户

4.更新security登录用户对象

5.生成token返回,将自定义user对象的名字头像换成QQ用户的

```java /* * 授权回调 / @GetMapping(value = "/connect") @ApiOperation("回调地址,授权自己专属url后自动跳转") public AjaxResult qqCallback(HttpServletRequest request,String code,String state) throws Exception { // 验证信息 String uuid = (String) request.getServletContext().getAttribute("state");

       // 验证信息我们发送的状态码
       if (uuid == null || state == null){
           return AjaxResult.error("登录出错了");
       }else if(null != uuid) {
           // 状态码不正确,直接返回登录页面
           if (!uuid.equals(state)) {
               return AjaxResult.error("登录出错了");
           }
       }
       //通过Authorization Code获取Access Token
       String url = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code" +
               "&client_id=" + APPID +
               "&client_secret=" + APPKEY +
               "&code=" + code +
               "&redirect_uri=" + URL;
       String access_token = QqHttpClient.getAccessToken(url);

       //获取回调后的openID
       url = "https://graph.qq.com/oauth2.0/me?access_token=" + access_token;
       String openId = QqHttpClient.getOpenID(url);


       //获取QQ用户信息
       url = "https://graph.qq.com/user/get_user_info?access_token=" + access_token +
               "&oauth_consumer_key=" + APPID +
               "&openid=" + openId;
       // 得到用户信息
       JSONObject QUser = QqHttpClient.getUserInfo(url);
       /**
        * 获取到用户信息之后,业务逻辑
        */
       //判断数据库是否存在此用户
       ThirdParty thirdParty = thirdPartyService.getOne(new QueryWrapper<ThirdParty>().eq("openid", openId));
       //不存在  返回请求让前端跳转绑定账号
       if (thirdParty == null ){
           ThirdParty thirdParty1 = new ThirdParty();
           thirdParty1.setLoginType("QQ");
           thirdParty1.setOpenid(openId);
           thirdParty1.setAccessToken(access_token);
           return  AjaxResult.error(505,"第一次登录绑定用户",thirdParty1);
       }
       //不是第一次登录  就直接登录
       Users users = usersService.getOne(new LambdaQueryWrapper<Users>().eq(Users::getId, thirdParty.getUserId()));
       if (users == null){
           return AjaxResult.error("非法用户,请联系管理员");
       }
       //开始登录
       UserDetails userDetails = userDetailsService.loadUserByUsername(users.getUsername());
       //更新security登录用户对象
       UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
               new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
       SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
       //生成token
       String token = jwtTokenUtil.generateToken(users.getUsername());
       SuccessUser successUser = new SuccessUser();
       HashMap<String,Object> map = new HashMap<>();
       Fllow fllow = usersService.queryByRoles(users.getUsername());
       Authority authority = new Authority(fllow.getRoles(),"0","","dashboard","","");
       successUser.setNickName(QUser.getString("nickname"));
       successUser.setHeaderImg(QUser.getString("figureurl_2"));
       successUser.setAuthority(authority);
       successUser.setSideMode("dark");
       successUser.setActiveColor("#1890ff");
       successUser.setBaseColor("#fff");
       System.out.println(successUser.getAuthority());
       map.put("user",successUser);
       map.put("token", token);
       return AjaxResult.success("登录成功",map);
   }

```

绑定当前系统账户

1.像登录接口一样,判断密码

2.判断QQ用户是否一定判定了或者用户已经绑定了QQ

3.绑定,登录

java @PostMapping(value = "/bangding") @ApiOperation("QQ第一次登录,绑定存在用户") public AjaxResult bangDing(String username, String password, @RequestBody ThirdParty thirdParty) throws IOException { //登录 //通过username获得userDetails对象 UserDetails userDetails = userDetailsService.loadUserByUsername(username); if(null == userDetails || !passwordEncoder.matches(password,userDetails.getPassword())){ return AjaxResult.error("用户名或密码不正确"); } String encode = passwordEncoder.encode(password); Users users = usersService.getOne(new LambdaQueryWrapper<Users>().eq(Users::getUsername, userDetails.getUsername())); if (users == null){ return AjaxResult.error("登录出错,请联系管理员"); } boolean result = thirdPartyService.checkUserId(users.getId()); if (!result){ return AjaxResult.error("此账号已经绑定了对应的QQ了"); } thirdParty.setUserId(Integer.valueOf(Math.toIntExact(users.getId()))); boolean save = thirdPartyService.save(thirdParty); if (!save){ return AjaxResult.error("绑定出错,请联系管理员"); } // Step4:获取QQ用户信息 String url = "https://graph.qq.com/user/get_user_info?access_token=" + thirdParty.getAccessToken() + "&oauth_consumer_key=" + APPID + "&openid=" + thirdParty.getOpenid(); // 得到用户信息 JSONObject QUser = QqHttpClient.getUserInfo(url); //接下来就是security登录操作,和上面一样 }

  1. 解绑逻辑在于确认用户密码,删除表中对应行就可以了

  2. QqHttpClient工具类

```java jiepublic class QqHttpClient { /* * 获取Access Token / public static String getAccessToken(String url) throws IOException { CloseableHttpClient client = HttpClients.createDefault(); String token = null;

       HttpGet httpGet = new HttpGet(url);
       HttpResponse response = client.execute(httpGet);
       HttpEntity entity = response.getEntity();

       if (entity != null) {
           String result = EntityUtils.toString(entity, "UTF-8");
           if (result.indexOf("access_token") >= 0) {
               String[] array = result.split("&");
               for (String str : array) {
                   if (str.indexOf("access_token") >= 0) {
                       token = str.substring(str.indexOf("=") + 1);
                       break;
                   }
               }
           }
       }

       httpGet.releaseConnection();
       return token;
   }

   /**
    * 获取openID
    */
   public static String getOpenID(String url) throws IOException {
       JSONObject jsonObject = null;
       CloseableHttpClient client = HttpClients.createDefault();

       HttpGet httpGet = new HttpGet(url);
       HttpResponse response = client.execute(httpGet);
       HttpEntity entity = response.getEntity();

       if (entity != null) {
           String result = EntityUtils.toString(entity, "UTF-8");
           jsonObject = parseJSONP(result);
       }

       httpGet.releaseConnection();

       if (jsonObject != null) {
           return jsonObject.getString("openid");
       } else {
           return null;
       }
   }

   /**
    * 获取QQ用户信息
    */
   public static JSONObject getUserInfo(String url) throws IOException {
       JSONObject jsonObject = null;
       CloseableHttpClient client = HttpClients.createDefault();

       HttpGet httpGet = new HttpGet(url);
       HttpResponse response = client.execute(httpGet);
       HttpEntity entity = response.getEntity();

       if (entity != null) {
           String result = EntityUtils.toString(entity, "UTF-8");
           jsonObject = JSONObject.parseObject(result);
       }

       httpGet.releaseConnection();

       return jsonObject;
   }

   /**
    * 转换json对象
    */
   private static JSONObject parseJSONP(String jsonp) {
       int startIndex = jsonp.indexOf("(");
       int endIndex = jsonp.lastIndexOf(")");
       String json = jsonp.substring(startIndex + 1, endIndex);
       return JSONObject.parseObject(json);
   }

} ```

总结

之前没接触过就感觉很难,主要是没思路,在网上搜了很多文档几乎都没有找到自己想要的。写完之后才感觉这么简单,说明了什么,人总会对自己的不同的领域有一定的恐惧但是也会有更大的好奇,只要好奇大过恐惧勇敢探索就会变得很简单咯。

希望能帮助到你,希望你也能年薪百万,不脱发,头发茂密。

自我介绍

本人大二,二本院校,热爱学习,编程。有没有哥哥推荐暑假的日常实习,就是算法很垃圾,其他的应该能接受。