Auth2.0加上免费的QQ登录
我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛
Auth2.0+QQ三方登录
QQ互联申请
申请开发者
这个一般不乱填都会通过的,一般就是四天左右吧。
应用管理—创建应用
这里我申请了很多次才通过,发现了技巧就好了,我们可以不用先管后端怎么写,先把回调申请下来再去写也可以。
- 首先要把QQ登录图片(官方文档里有)放在登录下面
-
将这个图片设置一个超链接https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=(自己appid)
-
将这个前端放在自己的服务器上,最好是80端口
-
下面就是填写申请表了,要想好一个回调地址这个需要和后台对应起来的非常重要,这个申请很快一般就是当天下午或者第二天下午就有结果
这上面基本百度都有教程的,但是下面代码部分我就很奇怪怎么没有人写过码
业务代码
我这是SpringSecurity项目上加的QQ三方登录,其实都一样的。
数据库设计
不需要修改原有的用户表,我们写一个专门存放第三方登录存储表
用户表和三方登录表一对一关系,把外键放哪里表都可以
代码设计 开发文档接口介绍
- yml配置文件写好自己的应用内容
yml
qq:
oauth:
appid: ********
appkey: *******
url: ********* #回调地址
- 代码逻辑
生成专属于自己的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登录操作,和上面一样
}
-
解绑逻辑在于确认用户密码,删除表中对应行就可以了
-
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);
}
} ```
总结
之前没接触过就感觉很难,主要是没思路,在网上搜了很多文档几乎都没有找到自己想要的。写完之后才感觉这么简单,说明了什么,人总会对自己的不同的领域有一定的恐惧但是也会有更大的好奇,只要好奇大过恐惧勇敢探索就会变得很简单咯。
希望能帮助到你,希望你也能年薪百万,不脱发,头发茂密。
自我介绍
本人大二,二本院校,热爱学习,编程。有没有哥哥推荐暑假的日常实习,就是算法很垃圾,其他的应该能接受。