Auth2.0加上免费的QQ登录

1,151 阅读5分钟

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

Auth2.0+QQ三方登录

QQ互联申请

申请开发者

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

应用管理—创建应用

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

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

image.png

  1. 将这个图片设置一个超链接graph.qq.com/oauth2.0/sh…

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

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

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

业务代码

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

数据库设计

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

image.png

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

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

    qq:
      oauth:
        appid: ********
        appkey: *******
        url: *********  #回调地址
    
  2. 代码逻辑

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

    /**
         * 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用户的

     /**
         * 授权回调
         */
        @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.绑定,登录

      @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登录操作,和上面一样
        }
    
  3. 解绑逻辑在于确认用户密码,删除表中对应行就可以了

  4. QqHttpClient工具类

    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);
        }
    
    }
    

总结

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

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

自我介绍

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