1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;
3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。
获取access_token时序图:
微信网站应用授权登录接口对接代码实现:
需用的配置文件:
需用的vo类:
代码:
package com.thinkwin.common.vo;
/**
* 类名: WechatAccessTokenVo * 描述: 授权关系接口的调用凭证模型 * 开发人员: weining * 创建时间: 2017/5/5 */
public class WechatAccessTokenVo{
// 网页授权接口调用凭证
private String accessToken ;
// 凭证有效时长
private int expiresIn;
// 用于刷新凭证
private String refreshToken;
// 用户标识
private String openId;
// 用户授权作用域
private String scope;
// 用户全局唯一标识 unionid
private String unionid;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getUnionid() {
return unionid;
}
public void setUnionid(String unionid) {
this.unionid = unionid;
}
}
package com.thinkwin.common.vo;
import java.util.List;
/**
* 类名: WechatSNSUserInfoVo * 描述: 通过网页授权获取的用户信息 * 开发人员: weining * 创建时间: 2017/4/27 */
public class WechatSNSUserInfoVo {
// 用户标识
private String openId;
// 用户昵称
private String nickname;
// 性别(1是男性,2是女性,0是未知)
private int sex;
// 国家
private String country;
// 省份
private String province;
// 城市
private String city;
// 用户头像链接
private String headImgUrl;
// 用户特权信息
private List privilegeList;
// 用户全局唯一标识 unionid
private String unionid;
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getHeadImgUrl() {
return headImgUrl;
}
public void setHeadImgUrl(String headImgUrl) {
this.headImgUrl = headImgUrl;
}
public List getPrivilegeList() {
return privilegeList;
}
public void setPrivilegeList(List privilegeList) {
this.privilegeList = privilegeList;
}
public String getUnionid() {
return unionid;
}
public void setUnionid(String unionid) {
this.unionid = unionid;
}
}
需要的工具类:
代码 :
package com.thinkwin.common.utils.wechat;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
/**
* 类名: WechatCommonUtil * 描述: 微信登录通用工具类 * 开发人员: weining * 创建时间: 2017/5/5 */
public class WechatCommonUtil {
private static Logger log = LoggerFactory.getLogger(WechatCommonUtil.class);
/**
* 发送https请求
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new WechatX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
log.error("连接超时:{}", ce);
} catch (Exception e) {
log.error("https请求异常:{}", e);
}
return jsonObject;
}
}
package com.thinkwin.common.utils.wechat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 类名: WechatConfigLoader * 描述: 微信初始化配置信息 * 开发人员: weining * 创建时间: 2017/5/5 */
public class WechatConfigLoader {
//日志记录对象
private static Logger log = LoggerFactory.getLogger(WechatConfigLoader.class);
//配置文件路径
private static String wechatPath = "wechat.properties";
//开发平台应用唯一标识
private static String appId;
//开放平台应用密钥
private static String appSecret;
//微信第三方回调地址
private static String backUrl;
static {
// 类初始化后加载配置文件
InputStream in = WechatConfigLoader.class.getClassLoader()
.getResourceAsStream(wechatPath);
Properties props = new Properties();
try {
props.load(in);
} catch (IOException e) {
log.error("load wechat setting error,pleace check the file path:"
+ wechatPath);
log.error(e.toString(), e);
}
appId = props.getProperty("wechat.appId");
appSecret = props.getProperty("wechat.appSecret");
backUrl = props.getProperty("wechat.backUrl");
log.debug("load wechat setting success,file path:" + wechatPath);
}
public static String getAppId() {
return appId;
}
public static String getAppSecret() {
return appSecret;
}
public static String getBackUrl() {
return backUrl;
}
public static void setWechatPath(String wechatPath) {
WechatConfigLoader.wechatPath = wechatPath;
}
public static String getWechatPath() {
return wechatPath;
}
public static void setAppId(String appId) {
WechatConfigLoader.appId = appId;
}
public static void setAppSecret(String appSecret) {
WechatConfigLoader.appSecret = appSecret;
}
public static void setBackUrl(String backUrl) {
WechatConfigLoader.backUrl = backUrl;
}
}
package com.thinkwin.lehui.common.wechatUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.thinkwin.lehui.common.wechatVo.WechatAccessTokenVo;
import com.thinkwin.lehui.common.wechatVo.WechatSNSUserInfoVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
/**
* 类名: WXOAuthProcess * 描述: 微信第三方登录授权流程工具类 * 开发人员: weining * 创建时间: 2017/5/5 */
public class WechatOAuthProcessUtil {
private static Logger log = LoggerFactory.getLogger(WechatOAuthProcessUtil.class);
/**
* 1.获取授权code
* @param req
* @param resp
*/
public static void getOAuthCode(HttpServletRequest req, HttpServletResponse resp){
String appId = WechatConfigLoader.getAppId();
String backUrl = WechatConfigLoader.getBackUrl();
String url="https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect";
url = url.replace("APPID",appId);
url = url.replace("REDIRECT_URI",URLEncoder.encode(backUrl));
try {
resp.sendRedirect(url);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 2.获取授权调用token
* @param appId 开发平台应用唯一标识
* @param appSecret 开放平台应用密钥
* @param code 授权临时票据 根据code来换取accessToken
*/
public static WechatAccessTokenVo getOauthAccessToken(String appId, String appSecret, String code){
WechatAccessTokenVo wechatAccessTokenVo = null;
//拼接微信获取accessToken请求的链接
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
url = url.replace("APPID",appId);
url = url.replace("SECRET",appSecret);
url = url.replace("CODE",code);
// 获取网页授权凭证 发送https请求
JSONObject jsonObject = (WechatCommonUtil.httpsRequest(url, "GET", null));
if (null != jsonObject) {
try {
wechatAccessTokenVo = new WechatAccessTokenVo();
wechatAccessTokenVo.setAccessToken(jsonObject.getString("access_token"));
wechatAccessTokenVo.setExpiresIn(jsonObject.getInteger("expires_in"));
wechatAccessTokenVo.setRefreshToken(jsonObject.getString("refresh_token"));
wechatAccessTokenVo.setOpenId(jsonObject.getString("openid"));
wechatAccessTokenVo.setScope(jsonObject.getString("scope"));
wechatAccessTokenVo.setUnionid(jsonObject.getString("unionid"));
} catch (Exception e) {
wechatAccessTokenVo = null;
int errorCode = jsonObject.getInteger("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return wechatAccessTokenVo;
}
/**
* 3.通过网页授权获取用户信息
*
* @param accessToken 网页授权接口调用凭证
* @param openId 用户标识
* @return SNSUserInfo
*/
@SuppressWarnings( { "deprecation", "unchecked" })
public static WechatSNSUserInfoVo getSNSUserInfo(String accessToken, String openId) {
WechatSNSUserInfoVo snsUserInfo = null;
// 拼接请求地址 发送https请求
String url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
url = url.replace("ACCESS_TOKEN", accessToken);
url = url.replace("OPENID", openId);
// 通过网页授权获取用户信息
JSONObject jsonObject = WechatCommonUtil.httpsRequest(url, "GET", null);
if (null != jsonObject) {
try {
snsUserInfo = new WechatSNSUserInfoVo();
// 用户的标识
snsUserInfo.setOpenId(jsonObject.getString("openid"));
// 昵称
snsUserInfo.setNickname(jsonObject.getString("nickname"));
// 性别(1是男性,2是女性,0是未知)
snsUserInfo.setSex(jsonObject.getInteger("sex"));
// 用户所在国家
snsUserInfo.setCountry(jsonObject.getString("country"));
// 用户所在省份
snsUserInfo.setProvince(jsonObject.getString("province"));
// 用户所在城市
snsUserInfo.setCity(jsonObject.getString("city"));
// 用户头像
snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));
// 用户特权信息
snsUserInfo.setPrivilegeList(JSONArray.toJavaObject(jsonObject.getJSONArray("privilege"), List.class));
} catch (Exception e) {
snsUserInfo = null;
int errorCode = jsonObject.getInteger("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return snsUserInfo;
}
/**
* 刷新授权调用token
* @param appId 开发平台应用唯一标识
* @param refreshToken 通过access_token获取到的refresh_token参数
*/
public static WechatAccessTokenVo refreshAccessToken(String appId, String refreshToken){
WechatAccessTokenVo wechatAccessTokenVo = null;
//拼接微信刷新accessToken请求的链接
String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";
url = url.replace("APPID",appId);
url = url.replace("REFRESH_TOKEN",refreshToken);
// 获取网页授权凭证 发送https请求
JSONObject jsonObject = WechatCommonUtil.httpsRequest(url, "GET", null);
if (null != jsonObject) {
try {
wechatAccessTokenVo = new WechatAccessTokenVo();
wechatAccessTokenVo.setAccessToken(jsonObject.getString("access_token"));
wechatAccessTokenVo.setExpiresIn(jsonObject.getInteger("expires_in"));
wechatAccessTokenVo.setRefreshToken(jsonObject.getString("refresh_token"));
wechatAccessTokenVo.setOpenId(jsonObject.getString("openid"));
wechatAccessTokenVo.setScope(jsonObject.getString("scope"));
} catch (Exception e) {
wechatAccessTokenVo = null;
int errorCode = jsonObject.getInteger("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return wechatAccessTokenVo;
}
}
package com.thinkwin.common.utils.wechat;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* 类名: MyX509TrustManager * 描述: 信任管理器 * 开发人员: weining * 创建时间: 2017/5/5 */
public class WechatX509TrustManager implements X509TrustManager {
// 检查客户端证书
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
// 检查服务器端证书
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
// 返回受信任的X509证书数组
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
最后就是controller控制层代码:
/**
* 确认请求来自微信服务器 微信的回调
*/
@RequestMapping(value = "/oauthtest", method = RequestMethod.GET)
public String OAuthTest(HttpServletRequest request, HttpServletResponse response, Model model) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
// 用户同意授权后,能获取到code
String code = request.getParameter("code");
String state = request.getParameter("state");
// 用户同意授权
if (!"authdeny".equals(code)) {
// 获取网页授权access_token
WechatAccessTokenVo wechatAccessTokenVo = WechatOAuthProcessUtil.getOauthAccessToken(code);
if(null == wechatAccessTokenVo){
if(state.equals("register")){
return "redirect:/system/wechatregisterpage";
}
return "redirect:/system/loginpage";
}
// 网页授权接口访问凭证
String accessToken = wechatAccessTokenVo.getAccessToken();
// 用户标识
String openId = wechatAccessTokenVo.getOpenId();
// 获取用户信息
WechatSNSUserInfoVo snsUserInfo = WechatOAuthProcessUtil.getSNSUserInfo(accessToken, openId);
Map map = new HashMap<>();
map.put("wechatAccessTokenVo",wechatAccessTokenVo);
map.put("snsUserInfo",snsUserInfo);
String s = JSON.toJSONString(map);
// 用户unionid
String unionId = snsUserInfo.getUnionid();
//把字符串存redis里面
RedisUtil.set("WeChat"+unionId,s);
//访问数据库操作
//直接登录
return "登录页";
}
return null;
}
到这里java基于微信开放平台的授权登录功能就完成了。