JAVA电商秒杀实战第三节——第三方登录

JAVA电商秒杀实战第三节——第三方登录

第三方登录 — QQ登录

准备工作

  1. 看QQ官方文档链接学习如果连接第三方QQ登录: https://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0.

  2. 放置QQ登录按钮
    获取AccessToken
    点击登录的按钮之后就会进入后端的loginByQQ()方法。
    以GET方式访问 https://graph.qq.com/oauth2.0/authorize,在后面添加参数
    在这里插入图片描述

  3. 申请appid和appkey

appid:
应用的唯一标识。在OAuth2.0认证过程中,appid的值即为oauth_consumer_key的值。

appkey:
appid对应的密钥,访问用户资源时用来验证应用的合法性。在OAuth2.0认证过程中,appkey的值即为oauth_consumer_secret的值。

代码实现

添加依赖:

//QQ
<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
</dependency>

定义一个常用类,保存一下官方提供的网址,给其他地方调用:

public final class LoginQQConstant {
    private LoginQQConstant(){}

    /**
     * 登录处理回调地址,可自定义,此时是为了演示方便,实际开发不应该放在常量类中,应放在配置文件
     */
    public static final String CALLBACK_URL ="http://127.0.0.1:8080/login/callbackHandler";

    /**
     * 自己的appID和appKey
     */
    public static final String APP_ID = "自己的APP-ID";
    public static final String APP_KEY = "自己的APP-KEY";

    /**
     * 获取Authorization Code
     */
    public static final String GET_AUTHORIZATION_CODE = "https://graph.qq.com/oauth2.0/authorize";

    /**
     * 通过Authorization Code获取Access Token
     */
    public static final String GET_ACCESS_TOKEN = "https://graph.qq.com/oauth2.0/token";

    /**
     * 获取用户openId
     */
    public static final String GET_OPEN_ID = "https://graph.qq.com/oauth2.0/me";

    /**
     * 获取用户信息
     */
    public static final String GET_USER_INFO = "https://graph.qq.com/user/get_user_info";
}

后端进行业务逻辑处理,基本步骤就是先根据官方文档中提供的url,按要求添加参数进行访问,然后获得返回的数据。
Contoller类:

@Controller
@RequestMapping("/login")
public class LoginController {


    @Autowired
    private RestTemplate restTemplate;

    /**
     * 处理登录事件,点击前端的图片会访问到这个方法
     */
    @RequestMapping("/loginByQQ")
    public void loginByQQ(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        String response_type = "code";
        String client_id = LoginQQConstant.APP_ID;
        // 官方文档强调此处要对url进行URLEncode,我试了一下,直接使用原始回调地址也能正常使用
        String redirect_uri = URLEncoder.encode(LoginQQConstant.CALLBACK_URL, "UTF-8");
//        String redirect_uri = LoginQQConstant.CALLBACK_URL;
        //client端的状态值。用于第三方应用防止CSRF攻击。
        String state = new Date().toString();
        req.getSession().setAttribute("state", state);

        String url = String.format(LoginQQConstant.GET_AUTHORIZATION_CODE +
                "?response_type=%s&client_id=%s&redirect_uri=%s&state=%s", response_type, client_id, redirect_uri, state);

        resp.sendRedirect(url);

        // 如果一切顺利,就会进入callbackHandler方法
    }

    /**
     * 用户授权后的回调方法
     */
    @ResponseBody
    @RequestMapping("/callbackHandler")
    public String callbackHandler(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        // 1.获取回调的authorization
        String authorization_code = req.getParameter("code");
        if (authorization_code == null || authorization_code.trim().isEmpty()) {
            throw new RuntimeException("未获取到AuthorizationCode");
        }
        // 2.client端的状态值。用于第三方应用防止CSRF攻击。
        String state = req.getParameter("state");
        if (!state.equals(req.getParameter("state"))) {
            throw new RuntimeException("client端的状态值不匹配!");
        }

        // 3.获取accessToken
        String urlForAccessToken = getUrlForAccessToken(authorization_code);
        String access_token = getAccessToken(urlForAccessToken);

        // 4.根据accessToken获取openId
        if (access_token == null || access_token.trim().isEmpty()) {
            throw new RuntimeException("未获取到accessToken");
        }
        String openid = getOpenId(access_token);

        // 5.根据openid获取用户信息
        if (openid == null || openid.trim().isEmpty()) {
            throw new RuntimeException("未获取到openId");
        }
        String userInfo = getUserInfo(openid,access_token);
        return "userInfo为:" + userInfo;

        // ... 获取到用户信息就可以进行自己的业务逻辑处理了
    }

    // 下面是辅助方法

    /**
     * 拼接用于获取accessToken的链接
     */
    private String getUrlForAccessToken(String authorization_code) throws UnsupportedEncodingException {
        String grant_type = "authorization_code";
        String client_id = LoginQQConstant.APP_ID;
        String client_secret = LoginQQConstant.APP_KEY;
//        String redirect_uri = URLEncoder.encode(CALLBACK_URL, "UTF-8"); 此处进行URLEncode会导致无法获取AccessToken
        String redirect_uri = LoginQQConstant.CALLBACK_URL;

        String url = String.format(LoginQQConstant.GET_ACCESS_TOKEN +
                        "?grant_type=%s&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s",
                grant_type, client_id, client_secret, authorization_code, redirect_uri);

        return url;
    }

    /**
     * 获取accessToken
     */
    private String getAccessToken(String urlForAccessToken) {
        //第一次用服务器发起模拟客户端访问,得到的是包含access_token的字符串,其格式如下
        //access_token=0FFD92ABD1DFD4F5&expires_in=7776000&refresh_token=04CE5D1F1E290B0974C5
        String firstCallbackInfo = restTemplate.getForObject(urlForAccessToken, String.class);
        String[] params = firstCallbackInfo.split("&");
        String access_token = null;
        for (String param : params) {
            String[] keyvalue = param.split("=");
            if (keyvalue[0].equals("access_token")) {
                access_token = keyvalue[1];
                break;
            }
        }
        return access_token;
    }

    /**
     * 根据accessToken获取openid
     */
    private String getOpenId(String access_token) throws IOException {
        String url = String.format(LoginQQConstant.GET_OPEN_ID + "?access_token=%s", access_token);
        //第二次模拟客户端发出请求后得到的是带openid的返回数据,格式如下
        //callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
        String secondCallbackInfo = restTemplate.getForObject(url, String.class);

        //正则表达式处理
        String regex = "\\{.*\\}";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(secondCallbackInfo);
        if (!matcher.find()) {
            throw new RuntimeException("异常的回调值: " + secondCallbackInfo);
        }

        //调用jackson
        ObjectMapper objectMapper = new ObjectMapper();
        HashMap hashMap = objectMapper.readValue(matcher.group(0), HashMap.class);

        String openid = ((String) hashMap.get("openid"));
        //return "获取到的openid为:" + openid;
        return openid;
    }

    /**
     * 根据openid获取用户信息
     */
    private String getUserInfo(String openid,String access_token) {
        String infoUrl = String.format(LoginQQConstant.GET_USER_INFO + "?access_token=%s&oauth_consumer_key=%s&openid=%ss", access_token, LoginQQConstant.APP_ID, openid);
        String userInfo = restTemplate.getForObject(infoUrl, String.class);
        return userInfo;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值