微信公众平台授权:
1、静默授权
静默授权不需要用户确认,只需要用户访问某个网页,属于嵌套在普通网页里的授权形式,但是只能获取到用户的唯一标示openid,无法获取用户的个人信息。
2、网页授权
2.1 网页授权流程
2.2.1、引导用户进入授权页面同意授权,获取code
2.2. 2、通过code换取网页授权access_token(与基础支持中的access_token不同)
2.2. 3、如果需要,开发者可以刷新网页授权access_token,避免过期
2.2.4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
获取到code后,后面的操作就比较简单,注意 用户授权的超链接地址参数问题。
微信官方:微信网页授权地址 点击打开链接
2.2 详细流程
2.2.1 用户同意授权,获取code
在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识 |
redirect_uri | 是 | 重定向地址,需要进行UrlEncode (包含在超链接中的第三方网站的地址)请使用 urlEncode 对链接进行处理 |
response_type | 是 | 填code |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 |
超链接eg:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx7c196d12a16e5&redirect_uri=http%3a%2f%2fm.hacn.cn%2fwxss%2flogin&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect
返回说明
用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数
redirect_uri?code=CODE&state=STATE
若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数
redirect_uri?state=STATE
第二种获取code的方式,支持网站将微信登录二维码内嵌到自己页面中,用户使用微信扫码授权后通过JS将code返回给网站。
JS微信登录主要用途:网站希望用户在网站内就能完成登录,无需跳转到微信域下登录后再返回,提升微信登录的流畅性与成功率。 网站内嵌二维码微信登录JS实现办法:
步骤1:在页面中先引入如下JS文件(支持https):
<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
步骤2:在需要使用微信登录的地方实例以下JS对象:
var obj = new WxLogin({
id:"login_container",
appid: "",
scope: "",
redirect_uri: "",
state: "",
style: "",
href: ""
});
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
id | 是 | 第三方页面显示二维码的容器id |
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 |
redirect_uri | 是 | 重定向地址,需要进行UrlEncode |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 |
style | 否 | 提供"black"、"white"可选,默认为黑色文字描述。详见文档底部FAQ |
href | 否 | 自定义样式链接,第三方可根据实际需求覆盖默认样式。详见文档底部FAQ |
附上获取 token 代码
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service("weiXinAuthorizeService")
public class WeiXinAuthorizeServiceImpl implements WeiXinAuthorizeService {
private static Logger logger = LoggerFactory.getLogger(WeiXinAuthorizeServiceImpl.class);
/**
* 通过code换取网页授权access_token
*首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。
*尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。
*/
@Override
public String getAccessToken(String code, String appid, String secret) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("appid", appid));
params.add(new BasicNameValuePair("secret", secret));
params.add(new BasicNameValuePair("code", code));
params.add(new BasicNameValuePair("grant_type", GrantType.AUTHORIZATION_CODE.getCode()));
return remoteCall(ACCESS_TOKEN, params);
}
/**
* 刷新access_token(如果需要)
*由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。
*/
@Override
public String getRefreshToken(String appid, String refreshToken) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("appid", appid));
params.add(new BasicNameValuePair("refresh_token", refreshToken));
params.add(new BasicNameValuePair("grant_type", GrantType.REFRESH_TOKEN.getCode()));
return remoteCall(REFRESH_TOKEN, params);
}
/**
* 检验授权凭证(access_token)是否有效
*/
@Override
public String checkIsValidToken(String openid, String accessToken) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("openid", openid));
params.add(new BasicNameValuePair("access_token", accessToken));
return remoteCall(TOKEN_IS_VALID, params);
}
private String remoteCall(String action, List<NameValuePair> params) {
String resultJson = "";
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
// Get请求
HttpGet httpGet = new HttpGet(WEI_XIN_URL + action);
String str = EntityUtils.toString(new UrlEncodedFormEntity(params));
httpGet.setURI(new URI(httpGet.getURI().toString() + "?" + str));
CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
try {
// response实体
HttpEntity entity = httpResponse.getEntity();
if (null != entity) {
logger.debug("微信接口响应状态码:" + action + "," + httpResponse.getStatusLine());
resultJson = EntityUtils.toString(entity,"UTF-8");
}
} finally {
httpResponse.close();
}
} catch (Exception e) {
logger.error("微信接口调用失败:" + e.getLocalizedMessage());
e.printStackTrace();
} finally {
try {
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return resultJson;
}
}