SpringBoot配置整合第三方QQ登陆以及返回登陆前的页面

目前各种网站登陆都有QQ登陆方式,那我们这篇文章就是教如何QQ登陆和登陆成功后返回登陆前的页面
思路:对于QQ登陆,目前是这样的,我们组成信息登陆信息,发给QQ互联,然后QQ互联返回来,我们验证发回来的信息并保存数据库,登陆成功返回登陆前的页面,登陆失败,在提示失败。

前端的代码:

  1. function goto() {
  2. //获取当前位置
  3. var url=window.location.href;
  4. //访问后台接口
  5. window.open('/api/qq?url='+url,'_self');
  6. }

后台代码

  1. @Controller
  2. @RequestMapping("/api")
  3. public class QqLoginController extends BaseController {
  4. @Autowired
  5. private NBContext blogContext;
  6. @Autowired
  7. private ParamRepository paramRepository;
  8. @Autowired
  9. private QqLoginService qqLoginService;
  10. @GetMapping("/qq")
  11. public void qqLogin(HttpServletRequest request,HttpServletResponse response,String url) throws IOException {
  12. //保存登陆前的地址到response的cookie中
  13. CookieUtils.setCookie(response, "backUrl",url, -1);
  14. String callbackDomain = basePath(request).concat("api/qqCallback");
  15. String appId ="XXXXXXX"; //QQ互联注册的APPID
  16. response.sendRedirect("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" + appId + "&redirect_uri=" + callbackDomain + "&state=" + System.currentTimeMillis());//用上面的response重定向访问
  17. }
  18. @GetMapping("/qqCallback")
  19. public String qqCallback(HttpServletRequest request, HttpServletResponse response, String code) {
  20. //QQ互联回调回来的接口,我们可以获取刚才访问给QQ互联的Cookie,拿到登陆前的地址
  21. Cookie cookie = CookieUtils.getCookie(request, "backUrl");
  22. String url="frontend/succes";
  23. if (cookie != null) {
  24. url=cookie.getValue(); //获取
  25. }
  26. String callbackDomain = basePath(request).concat("api/qqCallback");
  27. NBR r = qqLoginService.login(callbackDomain, code);
  28. if (r.get(NBR.CODE).equals(NBR.SUCCESS)) {
  29. blogContext.setSessionUser(request, response, (NBSysUser) r.get(NoteBlogV4.Session.LOGIN_USER));
  30. long masterRoleId = blogContext.getApplicationObj(NoteBlogV4.Session.WEBMASTER_ROLE_ID);
  31. if ( ((NBSysUser) r.get(NoteBlogV4.Session.LOGIN_USER)).getDefaultRoleId() == masterRoleId)
  32. return NoteBlogV4.Session.MANAGEMENT_INDEX;
  33. else
  34. return "redirect:" + url;//如果成功,重定向到登陆前的地址
  35. } else {
  36. return "error/page";
  37. }
  38. }
  39. }
  40. qqLoginService.login(callbackDomain, code)的代码如下
  41. public NBR login(String callbackDomain, String code) {
  42. try {
  43. String appId = "XXXXXXX";
  44. String appKey = "XXXXXXXXX"
  45. Map<String, Object> p1 = Maps.hashMap("grant_type", "authorization_code", "client_id", appId, "client_secret", appKey, "code", code, "redirect_uri", callbackDomain);
  46. String resp1 = HttpUtil.get("https://graph.qq.com/oauth2.0/token", p1);
  47. String accessToken = resp1.substring(13, resp1.length() - 66);
  48. String callback = HttpUtil.get("https://graph.qq.com/oauth2.0/me", Maps.hashMap("access_token", accessToken));
  49. String openId = callback.substring(45, callback.length() - 6);
  50. Map<String, Object> p2 = Maps.hashMap("access_token", accessToken, "oauth_consumer_key", appId, "openid", openId);
  51. JSONObject json2 = JSONUtil.parseObj(HttpUtil.get("https://graph.qq.com/user/get_user_info", p2));
  52. if (json2.getInt("ret") == 0) {
  53. NBSysUser user = userRepository.findByQqOpenIdAndEnable(openId, true);//查看数据库是否有这openId的用户 if (user != null) {
  54. return NBR.ok("授权成功!", "/").put(NoteBlogV4.Session.LOGIN_USER,user); //非空,则之前有登陆过
  55. } else {
  56. NBSysUser lockedUser = userRepository.findByQqOpenIdAndEnable(openId, false);
  57. if (lockedUser != null) {
  58. return NBR.error("QQ登录授权失败,原因:用户已被锁定!");
  59. }
  60. String nickname = json2.getStr("nickname");
  61. String avatar = json2.getStr("figureurl_qq_2").replace("http://", "https://");
  62. NBSysUser registerUser = NBSysUser.builder().nickname(nickname).avatar(avatar).qqOpenId(openId).build();
  63. NBSysUser afterRegisterUser = userRepository.save(registerUser);
  64. if (afterRegisterUser != null) {
  65. return NBR.ok("授权成功!", "/").put(NoteBlogV4.Session.LOGIN_USER,afterRegisterUser);
  66. } else {
  67. return NBR.error("QQ登录授权失败,原因:注册失败!");
  68. }
  69. }
  70. } else {
  71. String errorMsg = json2.getStr("msg");
  72. log.error("QQ登录授权失败,原因:{}", errorMsg);
  73. return NBR.error("QQ登录授权失败,原因:{}", errorMsg);
  74. }
  75. } catch (StringIndexOutOfBoundsException e) {
  76. log.error("[accessToken] 返回值有误!");
  77. return NBR.error("请求重复或返回 [accessToken] 值有误!");
  78. } catch (Exception e) {
  79. log.error("QQ登录授权失败,原因:{}", e);
  80. return NBR.error("QQ登录授权失败,原因:{}", e.getMessage());
  81. }
  82. }

NBR 类是内部的通信类,如下:

  1. public class NBR extends ConcurrentHashMap<String, Object> {
  2. public static final String CODE = "code";
  3. public static final String MESSAGE = "message";
  4. public static final String DATA = "data";
  5. public static final int SUCCESS = 200;
  6. public static final int SERVER_ERROR = 500;
  7. private NBR() {
  8. put(CODE, SUCCESS);
  9. }
  10. @Override
  11. public NBR put(String key, Object value) {
  12. super.put(key, value);
  13. return this;
  14. }
  15. /**
  16. * 返回默认的成功响应的实体
  17. *
  18. * @return
  19. */
  20. public static NBR ok() {
  21. return new NBR();
  22. }
  23. /**
  24. * 返回默认的成功响应的实体,只带文本信息
  25. *
  26. * @param msg 文本信息
  27. * @return
  28. */
  29. public static NBR ok(String msg) {
  30. NBR nbr = new NBR();
  31. nbr.put("message", msg == null || "".equals(msg) ? "success!" : msg);
  32. return nbr;
  33. }
  34. /**
  35. * 返回默认的成功响应的实体,只带文本信息
  36. *
  37. * @param msg 文本信息
  38. * @param param 替换为本占位符参数
  39. * @return
  40. */
  41. public static NBR formatOk(String msg, Object... param) {
  42. NBR nbr = new NBR();
  43. //noinspection ConfusingArgumentToVarargsMethod
  44. nbr.put("message", msg == null || "".equals(msg) ? "success!" : StrUtil.format(msg, param));
  45. return nbr;
  46. }
  47. /**
  48. * 自定义成功响应数据,包含额外的返回数据
  49. *
  50. * @param data 额外的数据
  51. * @return 返回响应正确的实体
  52. */
  53. public static NBR ok(Object data) {
  54. return ok(null, data);
  55. }
  56. /**
  57. * 自定义成功响应数据,包含响应信息以及额外的返回数据
  58. *
  59. * @param msg 响应信息
  60. * @param data 额外的西湖局
  61. * @return 返回响应正确的实体
  62. */
  63. public static NBR ok(String msg, Object data) {
  64. return ok(msg).put(DATA, data);
  65. }
  66. /**
  67. * 自定义返回成功信息,带额外的map信息
  68. *
  69. * @param jsonMap map信息
  70. * @return
  71. */
  72. public static NBR ok(Map<String, Object> jsonMap) {
  73. NBR nbr = new NBR();
  74. nbr.putAll(jsonMap);
  75. return nbr;
  76. }
  77. /**
  78. * 自定义错误响应数据,带额外的数据(指定类型,默认状态码为500
  79. *
  80. * @param message 错误信息
  81. * @param 额外数据指定的类型
  82. * @return 返回响应错误的实体
  83. */
  84. public static <T> NBR error(String message, T data) {
  85. return error(message).put(DATA, data);
  86. }
  87. /**
  88. * 自定义错误响应数据,默认状态码为500
  89. *
  90. * @param message 错误信息
  91. * @return 返回响应错误的实体
  92. */
  93. public static NBR error(String message) {
  94. return Objects.requireNonNull(ok().put(CODE, SERVER_ERROR)).put(MESSAGE, message);
  95. }
  96. /**
  97. * 自定义响应数据,不带额外的参数
  98. *
  99. * @param code 状态码
  100. * @param data 额外数据
  101. * @return 静态方法,返回响应实体JSON数据
  102. */
  103. public static <T> NBR custom(int code, T data) {
  104. return Objects.requireNonNull(ok().put(CODE, code)).put(DATA, data);
  105. }
  106. /**
  107. * 自定义响应数据,不带额外的参数
  108. *
  109. * @param code 状态码
  110. * @param message 响应信息
  111. * @return 静态方法,返回响应实体JSON数据
  112. */
  113. public static NBR custom(int code, String message) {
  114. return Objects.requireNonNull(ok().put(CODE, code)).put(MESSAGE, message);
  115. }
  116. /**
  117. * 自定义响应数据,不带额外的参数
  118. *
  119. * @param code 状态码
  120. * @return 静态方法,返回响应实体JSON数据
  121. */
  122. public static NBR custom(int code) {
  123. return custom(code, "");
  124. }
  125. /**
  126. * 自定义响应数据,带额外的参数(指定类型)
  127. *
  128. * @param code 状态码
  129. * @param message 响应信息
  130. * @param data 额外的数据
  131. * @param 额外数据的指定类型
  132. * @return 静态方法,返回响应实体JSON数据
  133. */
  134. public static <T> NBR custom(int code, String message, T data) {
  135. return custom(code, message).put(DATA, data);
  136. }

}