OAuth2登录协议

OAuth2登录协议

本文以微信授权登录来演示说明

参考文档:

  1. 微信登录功能 / 网站应用微信登录开发指南 (qq.com)
  2. 微信登录开发者测试平台 (qq.com)

img

所用到的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.17</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>

所用到的model:

1
2
3
4
5
6
7
8
9
@Data
public class TokenInfo {
private String accessToken;
private Integer expiresIn;
private String refreshToken;
private String openid;
private String scope;

}
1
2
3
4
5
6
7
8
9
10
11
12
@Data
public class WeChatUser {
private String openid;
private String nickname;
private Integer sex;
private String province;
private String city;
private String country;
private String headimgurl;
private String privilege;
private String unionid;
}

信息配置

此步用于配置信息,这样才能和微信登录服务方连接

image-20231210194956981

1
2
3
4
5
6
7
8
9
10
@RequestMapping("/wxCheck")
public String wxSignatureCheck(
@RequestParam(value = "signature") String signature,
@RequestParam(value = "timestamp") String timestamp,
@RequestParam(value = "nonce") String nonce,
@RequestParam(value = "echostr") String echostr
){
log.info("收到微信校验请求,echostr:{}",echostr);
return echostr;
}

image-20231210195551627

image-20231210195641777

定义自己的登录接口

自定义登录接口,采用微信扫码登录的方式向微信服务端发起登录授权请求

1
2
3
4
5
6
7
8
9
10
11
@GetMapping("/wxLogin")
@ResponseBody
public void wxLoginPage(HttpServletResponse response) throws Exception{
String redirectUrl = URLEncoder.encode("https://2a644f9d.r15.cpolar.top/wxCallBack", StandardCharsets.UTF_8);
//该请求用于获取code,获取code之后会重定向到redirectUrl获取token
String url = " https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx120df5342711e687&redirect_uri="
+redirectUrl+"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
response.setContentType("image/png");
//用于生成登录二维码
QrCodeUtil.generate(url,300,300,"jpg",response.getOutputStream());
}
1
2
3
4
5
6
7
//用上一步获取的code去获取token
@RequestMapping("/wxCallBack")
@ResponseBody
public String pcCallback(String code, String state, HttpServletRequest request,HttpServletResponse response,HttpSession session) throws IOException {
WeChatUser userInfo = WechatUtil.getUserInfo(code);
return JSONUtil.toJsonStr(userInfo);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//具体获取token的操作
public static WeChatUser getUserInfo(String code) throws IOException {
String responseResult = "";
System.out.println("code = " + code);
RestTemplate restTemplate = new RestTemplate();
CloseableHttpClient httpClient = HttpClients.createDefault();
String tokenUrl ="https://api.weixin.qq.com/sns/oauth2/access_token?appid="+APP_ID+"&secret="+APP_SECRET+"&code="+code+"&grant_type=authorization_code";
HttpGet request = new HttpGet(tokenUrl);
CloseableHttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() == 200){
responseResult = EntityUtils.toString(response.getEntity(),"UTF-8");
}
log.info("获取到responseResult:{}",responseResult);
TokenInfo tokenInfo = JSONUtil.toBean(responseResult, TokenInfo.class);
String userInfoURL = "https://api.weixin.qq.com/sns/userinfo?access_token="+tokenInfo.getAccessToken()+"&openid="
+tokenInfo.getOpenid()+"&lang=zh_CN";

HttpGet request1 = new HttpGet(userInfoURL);
CloseableHttpResponse response1 = httpClient.execute(request1);
if (response1.getStatusLine().getStatusCode() == 200){
responseResult = EntityUtils.toString(response1.getEntity(),"UTF-8");
}
log.info("获取到userInfoResult:{}",responseResult);
WeChatUser userInfo = JSONUtil.toBean(responseResult, WeChatUser.class);
redisTemplate.opsForValue().set("token:"+tokenInfo.getAccessToken(), JSONUtil.toJsonStr(userInfo));
return userInfo;
}

BUG笔记

  1. 这里发送请求获取tokenInfo,weChatUser不能直接用redistemplate直接封装

因为返回的数据还需要进行一步操作response.getEntity(),tokenInfo和weChatUser在response的下一层

  1. 无法注入redistemplate

redis版本出错,建议不选择版本号,默认选择springboot自带的版本这样适应jdk,不容易出错

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
  1. 静态类注入redistemplate方法
1
2
3
4
5
6
7
8
9
10
11
@Component
public class WechatUtil {

private static StringRedisTemplate redisTemplate;

@Autowired
public void setRedisTemplate(StringRedisTemplate redisTemplate) {
WechatUtil.redisTemplate = redisTemplate;
}

}