准备工作
微信授权登录让微信用户使用微信身份安全登录第三方应用或网站,在微信用户授权登录已接入微信OAuth2.0的第三方应用后,第三方可以获取到用户的接口调用凭证(access_token),通过access_token可以进行微信开放平台授权关系接口调用,从而可实现获取微信用户基本开放信息和帮助用户实现基础开放功能等。
微信OAuth2.0授权登录目前支持authorization_code模式,适用于拥有server端的应用授权。该模式整体流程为:
第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
通过code参数加上AppID和AppSecret等,通过API换取access_token;
通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。
在这里插入图片描述
第一步:请求CODE
第三方使用网站应用授权登录前请注意已获取相应网页授权作用域(scope=snsapi_login),则可以通过在PC端打开以下链接:
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
若提示“该链接无法访问”,请检查参数是否填写错误,如redirect_uri的域名与审核时填写的授权域名不一致或scope不为snsapi_login。
参数说明
参数 是否必须 说明
appid 是 应用唯一标识
redirect_uri 是 请使用urlEncode对链接进行处理
response_type 是 填code
scope 是 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即
state 否 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验
返回说明
用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数
redirect_uri?code=CODE&state=STATE
若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数
redirect_uri?state=STATE
代码如下:
@RequestMapping(value = "/wxLoginPage")
public String wxKfLoginPage(HttpServletRequest request) throws Exception {
// 防止csrf攻击(跨站请求伪造攻击)
String state = UUID.randomUUID().toString().replaceAll("-", "");
HttpSession session = request.getSession();
//存入session
session.setAttribute("wechat-kf-state",state);
//普通微信三方地址
String url = "https://open.weixin.qq.com/connect/qrconnect?" +
"appid="+constants.getWeCatKfAppId() +
"&redirect_uri="+constants.getWeCatKfRedirectUrl()+
"&response_type=code" +
"&scope=snsapi_login" +
"&state="+state+
"#wechat_redirect";
return "redirect:"+url;
}
第二步:执行成功后会执行第一步的回调方法
@RequestMapping("/callBack")
public String callBack(HttpServletRequest request){
//得到code用于交换token
String code = request.getParameter("code");
String state = request.getParameter("state");
//判断state是否合法
String stateStr = (String)request.getSession().getAttribute("wechat-kf-state");
if(StringUtils.isEmpty(code)||StringUtils.isEmpty(state)||!state.equals(stateStr)){
request.setAttribute("msg","非法操作,请重新登录!");
return "userInfo";
}
//用户授权后的code换取token
Map token = wechatService.getAccessToken(code,constants.getWeCatKfAppId(),constants.getWeCatKfAppSecret());
//判断是否成功获取到了token
if(token.get("errcode")!=null){
request.setAttribute("msg","token获取失败,请重新登录");
return "userInfo";
}
if(StringUtils.isEmpty((String)token.get("access_token"))||StringUtils.isEmpty((String)token.get("openid"))){
request.setAttribute("msg","access_token拉取失败,请重新登录");
return "userInfo";
}
//刷新accesstoken
Map refreshToken = wechatService.refrehToken(constants.getWeCatKfAppId(), (String) token.get("refresh_token"));
//使用token交换获取用户信息
WxchatUser user = wechatService.getUserInfo(refreshToken);
//将用户信息入栈
request.setAttribute("user",user);
return "userInfo";
}
第三步:在回调方法中的通过code换取accesstoken、
/**
* 通过code获取token
* @return
*/
public Map getAccessToken(String code,String appid,String secret){
//构建请求数据
String urlToken = "https://api.weixin.qq.com/sns/oauth2/access_token";
Map t = new HashMap();
t.put("appid",appid);
t.put("secret",secret);
t.put("code",code);
t.put("grant_type","authorization_code");
//调用httpclient处理请求得到返回json数据
String returnJson = HttpClientUtil.doGet(urlToken, t);
Map token = (Map) JSONObject.parse(returnJson);
return token;
}
第四步:通过accesstoken和openid获取用户信息
/**
* 通过token获取用户信息
* @param token
* @return
*/
public WxchatUser getUserInfo(Map token){
String urlUser = "https://api.weixin.qq.com/sns/userinfo";
//构建请求数据
Map u = new HashMap();
u.put("access_token",token.get("access_token"));
u.put("openid",token.get("openid"));
u.put("lang","zh_CN");
//调用httpclient处理请求得到用户信息json数据
String user = HttpClientUtil.doGet(urlUser, u);
//转换成用户对象
WxchatUser wxchatUser = JSONObject.parseObject(user, WxchatUser.class);
return wxchatUser;
}
第五步:刷新token(可省略这步)
/**
* 刷新token处理
* @return
*/
public Map refrehToken(String appid,String refresh_token){
String urlRefresh = "https://api.weixin.qq.com/sns/oauth2/refresh_token";
//构建请求参数
Map m = new HashMap();
m.put("appid",appid);
m.put("grant_type","refresh_token");
m.put("refresh_token",refresh_token);
//调用httpclient发出请求
Map refreshToken = (Map)JSONObject.parse(HttpClientUtil.doGet(urlRefresh, m));
return refreshToken;
}