目前各种网站登陆都有QQ登陆方式,那我们这篇文章就是教如何QQ登陆和登陆成功后返回登陆前的页面
思路:对于QQ登陆,目前是这样的,我们组成信息登陆信息,发给QQ互联,然后QQ互联返回来,我们验证发回来的信息并保存数据库,登陆成功返回登陆前的页面,登陆失败,在提示失败。
前端的代码:
function goto() {
//获取当前位置
var url=window.location.href;
//访问后台接口
window.open('/api/qq?url='+url,'_self');
}
后台代码
@Controller
@RequestMapping("/api")
public class QqLoginController extends BaseController {
@Autowired
private NBContext blogContext;
@Autowired
private ParamRepository paramRepository;
@Autowired
private QqLoginService qqLoginService;
@GetMapping("/qq")
public void qqLogin(HttpServletRequest request,HttpServletResponse response,String url) throws IOException {
//保存登陆前的地址到response的cookie中
CookieUtils.setCookie(response, "backUrl",url, -1);
String callbackDomain = basePath(request).concat("api/qqCallback");
String appId ="XXXXXXX"; //QQ互联注册的APPID
response.sendRedirect("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" + appId + "&redirect_uri=" + callbackDomain + "&state=" + System.currentTimeMillis());//用上面的response重定向访问
}
@GetMapping("/qqCallback")
public String qqCallback(HttpServletRequest request, HttpServletResponse response, String code) {
//QQ互联回调回来的接口,我们可以获取刚才访问给QQ互联的Cookie,拿到登陆前的地址
Cookie cookie = CookieUtils.getCookie(request, "backUrl");
String url="frontend/succes";
if (cookie != null) {
url=cookie.getValue(); //获取
}
String callbackDomain = basePath(request).concat("api/qqCallback");
NBR r = qqLoginService.login(callbackDomain, code);
if (r.get(NBR.CODE).equals(NBR.SUCCESS)) {
blogContext.setSessionUser(request, response, (NBSysUser) r.get(NoteBlogV4.Session.LOGIN_USER));
long masterRoleId = blogContext.getApplicationObj(NoteBlogV4.Session.WEBMASTER_ROLE_ID);
if ( ((NBSysUser) r.get(NoteBlogV4.Session.LOGIN_USER)).getDefaultRoleId() == masterRoleId)
return NoteBlogV4.Session.MANAGEMENT_INDEX;
else
return "redirect:" + url;//如果成功,重定向到登陆前的地址
} else {
return "error/page";
}
}
}
qqLoginService.login(callbackDomain, code)的代码如下
public NBR login(String callbackDomain, String code) {
try {
String appId = "XXXXXXX";
String appKey = "XXXXXXXXX";
Map<String, Object> p1 = Maps.hashMap("grant_type", "authorization_code", "client_id", appId, "client_secret", appKey, "code", code, "redirect_uri", callbackDomain);
String resp1 = HttpUtil.get("https://graph.qq.com/oauth2.0/token", p1);
String accessToken = resp1.substring(13, resp1.length() - 66);
String callback = HttpUtil.get("https://graph.qq.com/oauth2.0/me", Maps.hashMap("access_token", accessToken));
String openId = callback.substring(45, callback.length() - 6);
Map<String, Object> p2 = Maps.hashMap("access_token", accessToken, "oauth_consumer_key", appId, "openid", openId);
JSONObject json2 = JSONUtil.parseObj(HttpUtil.get("https://graph.qq.com/user/get_user_info", p2));
if (json2.getInt("ret") == 0) {
NBSysUser user = userRepository.findByQqOpenIdAndEnable(openId, true);//查看数据库是否有这openId的用户 if (user != null) {
return NBR.ok("授权成功!", "/").put(NoteBlogV4.Session.LOGIN_USER,user); //非空,则之前有登陆过
} else {
NBSysUser lockedUser = userRepository.findByQqOpenIdAndEnable(openId, false);
if (lockedUser != null) {
return NBR.error("QQ登录授权失败,原因:用户已被锁定!");
}
String nickname = json2.getStr("nickname");
String avatar = json2.getStr("figureurl_qq_2").replace("http://", "https://");
NBSysUser registerUser = NBSysUser.builder().nickname(nickname).avatar(avatar).qqOpenId(openId).build();
NBSysUser afterRegisterUser = userRepository.save(registerUser);
if (afterRegisterUser != null) {
return NBR.ok("授权成功!", "/").put(NoteBlogV4.Session.LOGIN_USER,afterRegisterUser);
} else {
return NBR.error("QQ登录授权失败,原因:注册失败!");
}
}
} else {
String errorMsg = json2.getStr("msg");
log.error("QQ登录授权失败,原因:{}", errorMsg);
return NBR.error("QQ登录授权失败,原因:{}", errorMsg);
}
} catch (StringIndexOutOfBoundsException e) {
log.error("[accessToken] 返回值有误!");
return NBR.error("请求重复或返回 [accessToken] 值有误!");
} catch (Exception e) {
log.error("QQ登录授权失败,原因:{}", e);
return NBR.error("QQ登录授权失败,原因:{}", e.getMessage());
}
}
NBR 类是内部的通信类,如下:
public class NBR extends ConcurrentHashMap<String, Object> {
public static final String CODE = "code";
public static final String MESSAGE = "message";
public static final String DATA = "data";
public static final int SUCCESS = 200;
public static final int SERVER_ERROR = 500;
private NBR() {
put(CODE, SUCCESS);
}
@Override
public NBR put(String key, Object value) {
super.put(key, value);
return this;
}
/**
* 返回默认的成功响应的实体
*
* @return
*/
public static NBR ok() {
return new NBR();
}
/**
* 返回默认的成功响应的实体,只带文本信息
*
* @param msg 文本信息
* @return
*/
public static NBR ok(String msg) {
NBR nbr = new NBR();
nbr.put("message", msg == null || "".equals(msg) ? "success!" : msg);
return nbr;
}
/**
* 返回默认的成功响应的实体,只带文本信息
*
* @param msg 文本信息
* @param param 替换为本占位符参数
* @return
*/
public static NBR formatOk(String msg, Object... param) {
NBR nbr = new NBR();
//noinspection ConfusingArgumentToVarargsMethod
nbr.put("message", msg == null || "".equals(msg) ? "success!" : StrUtil.format(msg, param));
return nbr;
}
/**
* 自定义成功响应数据,包含额外的返回数据
*
* @param data 额外的数据
* @return 返回响应正确的实体
*/
public static NBR ok(Object data) {
return ok(null, data);
}
/**
* 自定义成功响应数据,包含响应信息以及额外的返回数据
*
* @param msg 响应信息
* @param data 额外的西湖局
* @return 返回响应正确的实体
*/
public static NBR ok(String msg, Object data) {
return ok(msg).put(DATA, data);
}
/**
* 自定义返回成功信息,带额外的map信息
*
* @param jsonMap map信息
* @return
*/
public static NBR ok(Map<String, Object> jsonMap) {
NBR nbr = new NBR();
nbr.putAll(jsonMap);
return nbr;
}
/**
* 自定义错误响应数据,带额外的数据(指定类型,默认状态码为500
*
* @param message 错误信息
* @param 额外数据指定的类型
* @return 返回响应错误的实体
*/
public static <T> NBR error(String message, T data) {
return error(message).put(DATA, data);
}
/**
* 自定义错误响应数据,默认状态码为500
*
* @param message 错误信息
* @return 返回响应错误的实体
*/
public static NBR error(String message) {
return Objects.requireNonNull(ok().put(CODE, SERVER_ERROR)).put(MESSAGE, message);
}
/**
* 自定义响应数据,不带额外的参数
*
* @param code 状态码
* @param data 额外数据
* @return 静态方法,返回响应实体JSON数据
*/
public static <T> NBR custom(int code, T data) {
return Objects.requireNonNull(ok().put(CODE, code)).put(DATA, data);
}
/**
* 自定义响应数据,不带额外的参数
*
* @param code 状态码
* @param message 响应信息
* @return 静态方法,返回响应实体JSON数据
*/
public static NBR custom(int code, String message) {
return Objects.requireNonNull(ok().put(CODE, code)).put(MESSAGE, message);
}
/**
* 自定义响应数据,不带额外的参数
*
* @param code 状态码
* @return 静态方法,返回响应实体JSON数据
*/
public static NBR custom(int code) {
return custom(code, "");
}
/**
* 自定义响应数据,带额外的参数(指定类型)
*
* @param code 状态码
* @param message 响应信息
* @param data 额外的数据
* @param 额外数据的指定类型
* @return 静态方法,返回响应实体JSON数据
*/
public static <T> NBR custom(int code, String message, T data) {
return custom(code, message).put(DATA, data);
}
}
原创来源:滴一盘技术