微信公众号开发:如何授权登录、获取用户信息(openid)

2019-05-2320:52:26APP与小程序开发Comments4,318 views字数 11727阅读模式

看一下实现的效果,在公众号中,用户进入你的应用之前,会弹出一个授权页面,当用户点击确认后,你就可以获取用户的信息。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信公众号开发:如何授权登录、获取用户信息(openid)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

首先访问微信测试号登录页面,通过打开自己手机的微信,扫一扫登录
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信公众号开发:如何授权登录、获取用户信息(openid)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

进入到测试号页面后,分别看到如下信息
【测试号信息】
appID:开发者ID,是公众号开发识别码,配合开发者密码可以调用微信公众号接口,如获取微信昵称等
appsecret:开发者密码,是检验公众号开发者身份的密码,具有极高的安全性。切记不要把密码交给第三方开发者或者编写到代码里。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信公众号开发:如何授权登录、获取用户信息(openid)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

【接口配置信息】
URL: 是开发者用来接收微信消息和事件的接口URL,要用域名不能用ip
Token:由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信公众号开发:如何授权登录、获取用户信息(openid)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

当你填完URL和Token点击提交后,微信会访问你填写的URL,所以要在后台写一个servlet来处理这个请求
处理请求的Controller文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

@Controller
@RequestMapping("wechat")
public class WeiXinController {
@RequestMapping(method = { RequestMethod.GET })
public void doGet(HttpServletRequest request, HttpServletResponse response) {
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
PrintWriter out = null;
try {
out = response.getWriter();
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
log.debug("weixin get success....");
out.print(echostr);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null)
out.close();
}
}
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信请求校验工具类文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 微信请求校验工具类
*/
public class SignUtil {
// 与接口配置信息中的Token要一致
private static String token = "mytoken";文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 验证签名
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

请求的处理程序写完后把项目重新打包发布到服务器上去,再提交你填写的URL和Token,接口配置信息就Ok啦
【JS接口安全域名】
域名:想调用jssdk(如想要通过微信公众号js接口获取地图等工具)必须得填写此域名,在此域名的范围内才能调用jssdk工具,注意这里必须是域名,不是带有http之类的URL。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信公众号开发:如何授权登录、获取用户信息(openid)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

【测试号二维码】
里面包含了测试号二维码以及已经关注了的用户信息。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信公众号开发:如何授权登录、获取用户信息(openid)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

【体验接口权限表】
这里主要介绍【网页服务】里面的【网页帐号】
网页帐号主要用来设置OAuth2.0里面的网页授权域名,用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。沙盒号回调地址支持域名和ip,正式公众号回调地址只支持域名。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信公众号开发:如何授权登录、获取用户信息(openid)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

微信公众号开发:如何授权登录、获取用户信息(openid)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

接下来需要编写自己的程序以获取关注此公众号的用户信息
需要编写5个类 WechatLoginController.java,UserAccessToken.java,WechatUser.java,WechatUtil.java以及MyX509TrustManager.java
【WechatLoginController】主要用来获取已关注此微信号的用户信息并做相应处理文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

@Controller
@RequestMapping("wechatlogin")
/**
* 获取关注公众号之后的微信用户信息的接口,如果在微信浏览器里访问
* https://open.weixin.qq.com/connect/oauth2/authorize?appid=您的appId&redirect_uri=http://o2o.yitiaojieinfo.com/o2o/wechatlogin/logincheck&role_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect
* 则这里将会获取到code,之后再可以通过code获取到access_token 进而获取到用户信息
*/
public class WechatLoginController {文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

private static Logger log = LoggerFactory.getLogger(WechatLoginController.class);文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

@RequestMapping(value = "/logincheck", method = { RequestMethod.GET })
public String doGet(HttpServletRequest request, HttpServletResponse response) {
log.debug("weixin login get...");
// 获取微信公众号传输过来的code,通过code可获取access_token,进而获取用户信息
String code = request.getParameter("code");
// 这个state可以用来传我们自定义的信息,方便程序调用,这里也可以不用
// String roleType = request.getParameter("state");
log.debug("weixin login code:" + code);
WechatUser user = null;
String openId = null;
if (null != code) {
UserAccessToken token;
try {
// 通过code获取access_token
token = WechatUtil.getUserAccessToken(code);
log.debug("weixin login token:" + token.toString());
// 通过token获取accessToken
String accessToken = token.getAccessToken();
// 通过token获取openId
openId = token.getOpenId();
// 通过access_token和openId获取用户昵称等信息
user = WechatUtil.getUserInfo(accessToken, openId);
log.debug("weixin login user:" + user.toString());
request.getSession().setAttribute("openId", openId);
} catch (IOException e) {
log.error("error in getUserAccessToken or getUserInfo or findByOpenId: " + e.toString());
e.printStackTrace();
}
}
// ======todo begin======
// 前面咱们获取到openId后,可以通过它去数据库判断该微信帐号是否在我们网站里有对应的帐号了,
// 没有的话这里可以自动创建上,直接实现微信与咱们网站的无缝对接。
// ======todo end======
if (user != null) {
// 获取到微信验证的信息后返回到指定的路由(需要自己设定)
return "frontend/index";
} else {
return null;
}
}
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

【UserAccessToken】用户AccessToken实体类,用来接收accesstoken以及openid等信息文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 用户授权token
*
*/
public class UserAccessToken {文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

// 获取到的凭证
@JsonProperty("access_token")
private String accessToken;
// 凭证有效时间,单位:秒
@JsonProperty("expires_in")
private String expiresIn;
// 表示更新令牌,用来获取下一次的访问令牌,这里没太大用处
@JsonProperty("refresh_token")
private String refreshToken;
// 该用户在此公众号下的身份标识,对于此微信号具有唯一性
@JsonProperty("openid")
private String openId;
// 表示权限范围,这里可省略
@JsonProperty("scope")
private String scope;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getAccessToken() {
return accessToken;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getExpiresIn() {
return expiresIn;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getRefreshToken() {
return refreshToken;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getOpenId() {
return openId;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setOpenId(String openId) {
this.openId = openId;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getScope() {
return scope;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setScope(String scope) {
this.scope = scope;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

@Override
public String toString() {
return "accessToken:" + this.getAccessToken() + ",openId:" + this.getOpenId();
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

【WechatUser】微信用户实体类,用来接收昵称 openid等用户信息文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 微信用户实体类
*/
public class WechatUser implements Serializable {
private static final long serialVersionUID = -4684067645282292327L;
// openId,标识该公众号下面的该用户的唯一Id
@JsonProperty("openid")
private String openId;
// 用户昵称
@JsonProperty("nickname")
private String nickName;
// 性别
@JsonProperty("sex")
private int sex;
// 省份
@JsonProperty("province")
private String province;
// 城市
@JsonProperty("city")
private String city;
// 区
@JsonProperty("country")
private String country;
// 头像图片地址
@JsonProperty("headimgurl")
private String headimgurl;
// 语言
@JsonProperty("language")
private String language;
// 用户权限,这里没什么作用
@JsonProperty("privilege")
private String[] privilege;
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getNickName() {
return nickName;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setNickName(String nickName) {
this.nickName = nickName;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public int getSex() {
return sex;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setSex(int sex) {
this.sex = sex;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getProvince() {
return province;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setProvince(String province) {
this.province = province;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getCity() {
return city;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setCity(String city) {
this.city = city;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getCountry() {
return country;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setCountry(String country) {
this.country = country;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getHeadimgurl() {
return headimgurl;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setHeadimgurl(String headimgurl) {
this.headimgurl = headimgurl;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String getLanguage() {
return language;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setLanguage(String language) {
this.language = language;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public String[] getPrivilege() {
return privilege;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void setPrivilege(String[] privilege) {
this.privilege = privilege;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

@Override
public String toString() {
return "openId:" + this.getOpenId() + ",nikename:" + this.getNickName();
}
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

【WechatUtil】主要用来提交https请求给微信获取用户信息文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 微信工具类
*/
public class WechatUtil {文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

private static Logger log = LoggerFactory.getLogger(WechatUtil.class);文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 获取UserAccessToken实体类
* @param code
* @return
* @throws IOException
*/
public static UserAccessToken getUserAccessToken(String code) throws IOException {
// 测试号信息里的appId
String appId = "您的appId";
log.debug("appId:" + appId);
// 测试号信息里的appsecret
String appsecret = "您的appsecret";
log.debug("secret:" + appsecret);
// 根据传入的code,拼接出访问微信定义好的接口的URL
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret
+ "&code=" + code + "&grant_type=authorization_code";
// 向相应URL发送请求获取token json字符串
String tokenStr = httpsRequest(url, "GET", null);
log.debug("userAccessToken:" + tokenStr);
UserAccessToken token = new UserAccessToken();
ObjectMapper objectMapper = new ObjectMapper();
try {
// 将json字符串转换成相应对象
token = objectMapper.readValue(tokenStr, UserAccessToken.class);
} catch (JsonParseException e) {
log.error("获取用户accessToken失败: " + e.getMessage());
e.printStackTrace();
} catch (JsonMappingException e) {
log.error("获取用户accessToken失败: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
log.error("获取用户accessToken失败: " + e.getMessage());
e.printStackTrace();
}
if (token == null) {
log.error("获取用户accessToken失败。");
return null;
}
return token;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 获取WechatUser实体类
* @param accessToken
* @param openId
* @return
*/
public static WechatUser getUserInfo(String accessToken, String openId) {
// 根据传入的accessToken以及openId拼接出访问微信定义的端口并获取用户信息的URL
String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId
+ "&lang=zh_CN";
// 访问该URL获取用户信息json 字符串
String userStr = httpsRequest(url, "GET", null);
log.debug("user info :" + userStr);
WechatUser user = new WechatUser();
ObjectMapper objectMapper = new ObjectMapper();
try {
// 将json字符串转换成相应对象
user = objectMapper.readValue(userStr, WechatUser.class);
} catch (JsonParseException e) {
log.error("获取用户信息失败: " + e.getMessage());
e.printStackTrace();
} catch (JsonMappingException e) {
log.error("获取用户信息失败: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
log.error("获取用户信息失败: " + e.getMessage());
e.printStackTrace();
}
if (user == null) {
log.error("获取用户信息失败。");
return null;
}
return user;
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 发起https请求并获取结果
* @param requestUrl
* 请求地址
* @param requestMethod
* 请求方式(GET、POST)
* @param outputStr
* 提交的数据
* @return json字符串
*/
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
log.debug("https buffer:" + buffer.toString());
} catch (ConnectException ce) {
log.error("Weixin server connection timed out.");
} catch (Exception e) {
log.error("https request error:{}", e);
}
return buffer.toString();
}
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

【MyX509TrustManager】主要继承X509TrustManager做https证书信任管理器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

/**
* 证书信任管理器(用于https请求)
*
*/
public class MyX509TrustManager implements X509TrustManager {文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

public X509Certificate[] getAcceptedIssuers() {
return null;
}
}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

之后重新打包一个新的war包并发布到服务器tomcat webapps目录下
发布成功后,关注你自己的测试号(即扫描测试号的那个二维码),然后在手机微信里面或者微信开发者工具里访问相应链接:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=您的appId&redirect_uri=WechatLoginController对应的地址&role_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect
---------------------
作者:半-夏
来源:CSDN
原文:https://blog.csdn.net/victoyr/article/details/89648017
版权声明:本文为博主原创文章,转载请附上博文链接!文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/12817.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/xcx/12817.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定