网页授权的回调域名

关于网页授权的两种scope的区别说明

  • 以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。

  • 以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

  • 用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

关于网页授权access_token和普通access_token的区别

  • 微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;

  • 其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。

关于UnionID机制

  • 请注意,网页授权获取用户基本信息也遵循UnionID机制。即如果开发者有在多个公众号,或在公众号、移动应用之间统一用户帐号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定公众号后,才可利用UnionID机制来满足上述需求。

  • UnionID机制的作用说明:如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为同一用户,对同一个微信开放平台下的不同应用(移动应用、网站应用和公众帐号),unionid是相同的。

关于特殊场景下的静默授权

  • 上面已经提到,对于以snsapi_base为scope的网页授权,就静默授权的,用户无感知;

  • 对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知。

网页授权流程

  • 第一步:用户同意授权,获取code

参考链接

scope为snsapi_base

1
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect

scope为snsapi_userinfo

1
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

尤其注意:跳转回调redirect_uri,应当使用https链接来确保授权code的安全性。

  • 将获取code,授权的请求做成二维码

制作二维码的工具类QRCodeGenerator

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Hashtable;

public class QRCodeGenerator {

public static void creatRrCode(String contents, int width, int height,HttpServletResponse response) {
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); //容错级别最高
hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); //设置字符编码
hints.put(EncodeHintType.MARGIN, 1); //二维码空白区域,最小为0也有白边,只是很小,最小是6像素左右
try {
// 1、读取文件转换为字节数组
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints);

BufferedImage image = toBufferedImage(bitMatrix);

//转换成png格式的IO流
ImageIO.write(image, "png", response.getOutputStream());
// byte[] bytes = out.toByteArray();
// 2、将字节数组转为二进制
// BASE64Encoder encoder = new BASE64Encoder();
// binary = encoder.encodeBuffer(bytes).trim();
} catch (WriterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


/**
* image流数据处理
*
* @author ianly
*/
public static BufferedImage toBufferedImage(BitMatrix matrix) {
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
return image;
}
}

显示二维码的页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>微信登录</title>
</head>
<body>
<img id="qrcode" style="padding-left: 20px" src="qrcode">
</body>
<script>
</script>
</html>

二维码的地址,后台controller代码

1
2
3
4
5
6
7
8
9
10
public String appid = "wxe6774713a448fb6f";
public String secret = "1fec63da4f2a53c3224e6adf6dd4876e";

@RequestMapping("qrcode")
public void login(HttpServletResponse response){
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?";
String redirect_uri = URLEncoder.encode("http://eeee.free.idcfengye.com/getCode");
String params = "appid="+appid+"&secret="+secret+"&redirect_uri="+redirect_uri+"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
QRCodeGenerator.creatRrCode(url+params, 180,180,response);
}

code传递二维码的redirect_uri,然后通过code获取access_token,再刷新access_token,最后拉取用户信息

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
28
29
30
31
32
33
34
//二维码地址的redirect_uri
@RequestMapping("/getCode")
public ModelAndView toSuccess(HttpServletRequest request){
String code = request.getParameter("code");

String url = "https://api.weixin.qq.com/sns/oauth2/access_token?";
String params = "appid="+appid+"&secret="+secret+"&code="+code+"&grant_type=authorization_code";

//调用httpGet方法发送url并返回json对象,可以获取access_token和refresh_token
JSONObject jsonObject = this.httpGet(url+params);
//使用refresh_token刷新access_token
String refresh_token = jsonObject.getString("refresh_token");

String url1 = "https://api.weixin.qq.com/sns/oauth2/refresh_token?";
String params1 = "appid="+appid+"&grant_type=refresh_token&refresh_token="+refresh_token;

//调用httpGet方法发送url并返回json对象,可以获取刷新后的access_token
JSONObject jsonObject1 = this.httpGet(url1+params1);
//使用刷新的access_token和openid获取用户信息
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");

String url2 = "https://api.weixin.qq.com/sns/userinfo?";
String params2 = "access_token="+access_token+"&openid="+openid+"&lang=zh_CN";

//调用httpGet方法发送url并返回json对象,获取用户信息
JSONObject jsonObject2 = this.httpGet(url2+params2);
//System.out.println(jsonObject2);

ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("success");
modelAndView.addObject("userInfo", jsonObject2);
return modelAndView;
}

使用htpClient发送url请求获取返回,用阿里的fastjson解析json

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
public JSONObject httpGet(String url) {
JSONObject jsonResult = null;
try {
//目前HttpClient最新版的实现类为CloseableHttpClient
CloseableHttpClient client = HttpClients.createDefault();
//get方法
HttpGet request = new HttpGet(url);
//post方法
//HttpPost request = new HttpPost(url);

//执行Request请求,CloseableHttpClient的execute方法返回的response都是CloseableHttpResponse类型
CloseableHttpResponse response = client.execute(request);

//判断响应的状态码是否为200
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
//用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串,防止服务器返回的数据带有中文,所以在转换的时候将字符集指定成utf-8就可以了
String strResult = EntityUtils.toString(response.getEntity(),"UTF-8");
//将字符串转换json对象
jsonResult = JSON.parseObject(strResult);
} else {
System.out.println("请求出错");
}
} catch (IOException e) {
e.printStackTrace();
}
return jsonResult;
}