`

Java微信公众平台开发之OAuth2.0网页授权

阅读更多

根据官方文档点击查看在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头,也不需要加具体的项目名,在域名空间的根目录放一个txt文件才能验证通过

详细源码点击查看

一、两种scope授权方式

 

  • 以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
  • 以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息

 

 

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

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

 

三、.用户同意授权,获取code

我这边是snsapi_userinfo发起的网页授权,redirect_uri必须用urlEncode处理下

/**
 * 
 * @author phil
 */
public abstract class AbstractParams {
	
	/**
	 * 返回请求参数
	 * @return
	 */
	public abstract Map<String,String> getParams() throws Exception;
}

获取授权code请求参数AuthCodeParams.java

/**
 * 获取授权code请求参数
 * 
 * @author phil
 */
public class AuthCodeParams extends AbstractParams{
	
	public static final String SCOPE_SNSAPIBASE = "snsapi_base"; // snsapi_base(不需要弹出授权页面,只能获取openid)
	public static final String SCOPE_SNSPAIUSERINFO = "snsapi_userinfo"; // 弹出授权页面(获取用户基本信息)
	public static final String SCOPE_SNSAPI_LOGIN = "snsapi_login"; //网页应用目前仅填写snsapi_login
	private String appid;
	private String redirect_uri; // 使用urlencode对链接进行处理
	private String response_type = "code"; 
	private String scope;
	private String state;
	
	public AuthCodeParams() {
		super();
	}

	public AuthCodeParams(String appid, String redirect_uri, String response_type, String scope, String state) {
		super();
		this.appid = appid;
		this.redirect_uri = redirect_uri;
		this.response_type = response_type;
		this.scope = scope;
		this.state = state;
	}

	/**
	 * 参数组装
	 * @return
	 */
	public Map<String, String> getParams() throws UnsupportedEncodingException {
		Map<String, String> params = new TreeMap<String, String>();
		params.put("appid", this.appid);
		params.put("redirect_uri", HttpRequestUtil.urlEncode( this.redirect_uri, HttpRequestUtil.DEFAULT_CHARSET));
		params.put("response_type", this.response_type);
		params.put("scope", this.scope);
		params.put("state", this.state);
		return params;
	}

	public String getAppid() {
		return appid;
	}

	public void setAppid(String appid) {
		this.appid = appid;
	}

	public String getRedirect_uri() {
		return redirect_uri;
	}

	public void setRedirect_uri(String redirect_uri) {
		this.redirect_uri = redirect_uri;
	}

	public String getResponse_type() {
		return response_type;
	}

	public String getScope() {
		return scope;
	}

	public void setScope(String scope) {
		this.scope = scope;
	}

	public String getState() {
		return state;
	}

	public void setState(String state) {
		this.state = state;
	}
}

 

尤其注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问;所以我都是用TreeMap拼接参数

请求链接的拼接

 

// 授权链接
private final String AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize";	
AuthCodeParams authCodeParams = new AuthCodeParams();
String redirect_uri = "http://www.liebesy.com/oauth/bindWxPhone.shtml";
authCodeParams.setRedirect_uri(redirect_uri);
authCodeParams.setAppid(WeiXinConfig.APP_ID);
authCodeParams.setScope(AuthCodeParams.SCOPE_SNSAPIBASE);// 采用静默授权
authCodeParams.setState(MD5Util.MD5Encode("ceshi", "")); //防止被攻击,用于校验
String url = oAuthService.getAuthPath(authCodeParams, AUTHORIZE_URL);
request.setAttribute("bindurl", url);

getAuthPath()方法

/**
 * 获取授权请求path
 * @param basic
 * @param path
 * @return
 * @throws Exception
 */
public String getAuthPath(AbstractParams basic, String path) throws Exception{
	Map<String,String> params = basic.getParams();
	path = HttpRequestUtil.setParmas(params, path,"")+"#wechat_redirect";
	return path;
}
如果用户同意授权,页面将跳转至redirect_uri/?code=CODE&state=STATE ,可以用request.getParameter("code")直接获取到code,在此之后检验state是否发生变化。code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期,可用redis、mc等缓存
HttpRequestUtil.java见点击打开链接

四、通过code换取网页授权access_token

首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。

获取授权请求token的请求参数AuthTokenParams.java

/**
 * 获取授权请求token的请求参数
 * 
 * @author phil
 */
public class AuthTokenParams extends AbstractParams {
	private String appid; //公众号的唯一标识
	private String secret; //公众号的appsecret
	private String code; //填写第一步获取的code参数
	private String grant_type = "authorization_code";

	public AuthTokenParams() {
		super();
	}

	public AuthTokenParams(String appid, String secret, String code, String grant_type) {
		super();
		this.appid = appid;
		this.secret = secret;
		this.code = code;
		this.grant_type = grant_type;
	}
	
	/**
	 * 参数组装
	 * @return
	 */
	public Map<String, String> getParams() {
		Map<String, String> params = new TreeMap<String, String>();
		params.put("appid", this.appid);
		params.put("secret", this.secret);
		params.put("code", this.code);
		params.put("grant_type", this.grant_type);
		return params;
	}

	public String getAppid() {
		return appid;
	}

	public void setAppid(String appid) {
		this.appid = appid;
	}

	public String getSecret() {
		return secret;
	}

	public void setSecret(String secret) {
		this.secret = secret;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public String getGrant_type() {
		return grant_type;
	}
}

授权token的返回结果AuthAccessToken.java

/**
 * 授权token的返回结果
 * @author phil
 * 
 */
public class AuthAccessToken {

	private String access_token; // 范围授权token
	private int expires_in; // 过时时间
	private String refresh_token;// 刷新token
	private String openid; // 用户的openid
	private String scope; // 范围
	private String unionid; //当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段

	public String getAccess_token() {
		return access_token;
	}

	public void setAccess_token(String access_token) {
		this.access_token = access_token;
	}

	public int getExpires_in() {
		return expires_in;
	}

	public void setExpires_in(int expires_in) {
		this.expires_in = expires_in;
	}

	public String getRefresh_token() {
		return refresh_token;
	}

	public void setRefresh_token(String refresh_token) {
		this.refresh_token = refresh_token;
	}

	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;
	}
}

获取网页授权凭证方法

/**
 * 获取网页授权凭证
 * @param basic
 * @param path
 * @return
 */
public AuthAccessToken getAuthAccessToken(AbstractParams basic, String path) {
	AuthAccessToken authAccessToken = null;
	//获取网页授权凭证
	try {
		//String path = HttpRequestUtil.setParmas(params, TOKENPATH, null);
		String result = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.GET_METHOD, path, basic.getParams(), null);
		if(result != null){		
			authAccessToken = new Gson().fromJson(result, AuthAccessToken.class);
		}
	} catch (Exception e) {
		logger.info("error"+e.getMessage());
	}
	return authAccessToken;
}

获取网页凭证

// 获取token的链接
private final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";

@RequestMapping(value = "bindWxPhone", method = { RequestMethod.GET })
public String prize(HttpServletRequest request, HttpServletResponse response, String url) throws Exception {
	AuthAccessToken authAccessToken = null;
	// 用户同意授权后可以获得code,检验state
	String code = request.getParameter("code");
	if(code==null){
		return null;
	}
	String state = request.getParameter("state");
	if(state.equals(MD5Util.MD5Encode("ceshi", ""))){
		AuthTokenParams authTokenParams = new AuthTokenParams();
		authTokenParams.setAppid("");
		authTokenParams.setSecret("");
		authTokenParams.setCode(code);
		authAccessToken = oAuthService.getAuthAccessToken(authTokenParams, ACCESS_TOKEN_URL);	
	}
	if(authAccessToken!=null){
		logger.info("网页授权的accessToken=" + authAccessToken.getAccess_token());
		logger.info("正在绑定的openid=" + authAccessToken.getOpenid());
	}
	return Common.BACKGROUND_PATH + "/system/wxuserprize/bindPhone";
}

五、刷新access_token(如果需要)

由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。

推荐:用redis、mc之类的存储code

 

/**
 * 刷新网页授权验证
 * @param basic  参数
 * @param path 请求路径
 * @return
 */
public String refreshAuthAccessToken(AbstractParams basic, String path) {	
	AuthAccessToken authAccessToken = null;
	//刷新网页授权凭证
	try {
		String result = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.GET_METHOD, path, basic.getParams(), null);
		if(result != null){				
			authAccessToken = new Gson().fromJson(result, AuthAccessToken.class);
		}
	} catch (Exception e) {
		authAccessToken = null;
		logger.info("error"+e.getMessage());
		
	}
	return authAccessToken==null?null:authAccessToken.getAccess_token();
}

六、拉取用户信息

如果网页授权作用域为snsapi_userinfo,则此时可以通过access_token和openid拉取用户信息了

通过网页授权获取的用户信息AuthUserInfo.java

/**
 * 通过网页授权获取的用户信息
 * 
 * @author phil
 */
public class AuthUserInfo {
	// 用户标识
	private String openid;
	// 用户昵称
	private String nickname;
	// 性别(1是男性,2是女性,0是未知)
	private String sex;
	// 国家
	private String country;
	// 省份
	private String province;
	// 城市
	private String city;
	// 用户头像链接
	private String headimgurl;
	// 用户特权信息
	private List<String> privilege;
	// 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段
	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 String getSex() {
		return sex;
	}

	public void setSex(String 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<String> getPrivilege() {
		return privilege;
	}

	public void setPrivilege(List<String> privilege) {
		this.privilege = privilege;
	}

	public String getUnionid() {
		return unionid;
	}

	public void setUnionid(String unionid) {
		this.unionid = unionid;
	}
}
//获取授权用户信息
private final String USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo";
/**
 * 通过网页授权获取用户信息
 * @param accessToken
 * @param openid
 * @return
 */
public AuthUserInfo getAuthUserInfo(String accessToken, String openid) {
	AuthUserInfo authUserInfo = null;	
	//通过网页授权获取用户信息
	Map<String, String> params = new TreeMap<String, String>();
	params.put("openid", openid);
	params.put("access_token", accessToken);
	String result = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.GET_METHOD, USERINFO_URL, params, null);		
	if(null != result){
		try {
			authUserInfo = new Gson().fromJson(result, AuthUserInfo.class);
		} catch (JsonSyntaxException e) {
			logger.info("transfer exception");				
		} 
	}
	return authUserInfo;
}

七、检验授权凭证(access_token)是否有效

//判断用户accessToken是否有效
private final String AUTH_URL = "https://api.weixin.qq.com/sns/auth";

/**
 *  检验授权凭证(access_token)是否有效
 * @param accessToken 网页授权接口调用凭证
 * @param openid		用户的唯一标识
 * @return  { "errcode":0,"errmsg":"ok"}表示成功     { "errcode":40003,"errmsg":"invalid openid"}失败
 */
public ResultState authToken(String accessToken,String openid){
	ResultState state = null;
	Map<String,String> params = new TreeMap<String,String>();
	params.put("access_token",accessToken);
	params.put("openid", openid);
	String json = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.GET_METHOD, AUTH_URL, params,"");
	if(json!=null){
		state = new Gson().fromJson(json, ResultState.class);
	}
	return state;
}

 

 

分享到:
评论

相关推荐

    Java 微信网页授权登陆 OAuth2.0源码

    下载 ngrok ...微信公众平台 https://mp.weixin.qq.com/cgi-bin/frame?t=advanced/dev_tools_frame&nav=10049&token=1958267125&lang=zh_CN 参考网址: http://www.cnblogs.com/lovebread/p/5513241.html ...

    基于springboot2.x的Oauth2.0一键授权登录和微信网页扫码支付测试开发案例(含源码)

    使用idea开发工具,基于springboot2.x、jwt鉴权、nginx集群,前后端分离的微信Oauth2.0一键登录和微信网页扫码支付测试开发demo

    微信网页授权oauth2.0版

    首先,去微信公众平台测试号管理注册公众号。 地址:http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index IDE:IntelliJ IDEA 2020.1.3 x64 (找度娘破解) 微信开发者工具 (官网去...

    微信公众号二次开发项目源码

    微信开发即微信公众平台开发,将企业信息、服务、活动等内容通过微信网页的方式进行表现,用户通过简单的设置,就能生成微信3G网站。通过微信公众平台将企业品牌展示给微信用户,减少宣传成本,建立企业与消费者、...

    微信公众平台开发 实例 java 开发资料

    在“微信公众平台开发 实例 java 开发资料”中,我们可以学习到以下几个核心知识点: 1. **微信公众平台基础**:了解微信公众平台的基本架构和工作原理,包括订阅号、服务号和企业号的区别,以及它们各自的功能和...

    Oauth2.0统一登录平台V1.2 Oauth20UnifiedLoginPlatformV12.rar

    Oauth2.0统一登录平台V1.2 源码描述: 一、源码介绍 1、OAuth2.0几乎成了当今第三方平台的一个标准中的标准(我不知道几年后会出3.0),那既然是一个标准,为什么就不能用一个相对标准的类库或项目来实现呢?翻遍...

    微信公众平台应用开发:方法、技巧与案例.(机械工业.柳峰)

     6.3 OAuth2.0网页授权 134  6.3.1 配置网页授权回调域名 135  6.3.2 网页授权流程 135  6.3.3 方法封装 139  6.3.4 案例:通过网页授权获取用户信息 145  6.4 生成带参数二维码 149  6.4.1 创建临时...

    微信群机器人源码,微信群机器人源码 最新,Java

    - 应用微信提供的OAuth2.0授权机制,确保安全访问用户和群组资源。 - 设计和实现消息处理逻辑,这可能涉及到事件驱动编程、多线程处理、异步通信等技术。 - 利用机器学习框架(如TensorFlow、PyTorch等)训练AI模型...

    JAVA版微信连wifi

    4. **OAuth2.0授权框架**:为了获取用户的微信授权,开发者可能需要利用OAuth2.0协议。这是一个标准的授权框架,允许第三方应用在用户授权的情况下获取访问令牌,从而访问用户的特定资源。理解并正确实现OAuth2.0...

    解密第三方登录-微信扫码登录

    1. 基于微信公众平台的扫码登录:让第三方应用投入微信的怀抱而设计的,这第三方应用指的是比如 android、ios、网站、系统等。 2. 基于微信开放平台的扫码登录:为了让程序员小伙伴利用微信自家技术(公众号、小程序...

    Oauth2统一登录平台 v1.2

    OAuth2.0几乎成了当今第三方平台的一个标准中的标准(我不知道几年后会出3.0),那既然是一个标准,为什么就不能用一个相对标准的类库或项目来实现呢?翻遍整个china的开源项目,就别说是C#了,连java、php都没有...

    微信开发之网页授权获取用户信息(二)

     找到“网页授权获取用户基本信息”,点击后面对应的“修改”,在弹框响应位置填写授权回调域名即可,此处的域名不需要加http:// (关于网页授权回调域名的说明详情可参考公众平台开发者文档) 2、获取授权  ...

    开放平台公共帐号授权

    总的来说,开放平台公共帐号授权涉及了用户身份验证、安全通信和权限管理等多个方面,开发者需要理解OAuth2.0协议,并结合具体的技术栈进行实现。通过学习和实践,我们可以创建安全、便捷的第三方登录功能,为用户...

    微信表白墙源码

    开发者需要熟悉微信开发者文档,掌握OAuth2.0授权协议,获取并验证用户的微信OpenID,实现用户身份的绑定。 2. **后端开发**: 后端通常采用PHP或Java等服务器端语言编写,负责处理用户的请求,包括表白的提交、...

    基于SpringBoot+mysql聊天微信小程序源码

    - **用户认证与授权**:微信小程序可以通过OAuth2.0与微信服务器交互获取用户信息,实现用户登录和权限控制。 - **数据库设计**:包括用户表、聊天室表、消息表等,合理设计表结构和关联关系以支持高效的查询和...

    最多跑一次微信小程序.zip

    这些接口的调用通常涉及OAuth2.0认证、微信支付SDK的使用以及微信服务器的交互。 5. **Java编程**:作为后端语言,Java在本项目中承担着数据处理和业务逻辑的重要角色。开发者需要熟练掌握Java面向对象编程、异常...

    基于ASP.NET的手机微信点餐收银系统源码.zip

    4. **微信接口**:集成微信公共平台API,实现用户登录授权、微信支付等功能,通常涉及OAuth2.0协议和微信支付SDK。 5. **配置文件**:如web.config,用于设置应用的环境变量、数据库连接字符串、错误处理等。 6. **...

    居民健康监测系统微信小程序.zip

    居民健康监测系统微信小程序是一个基于Java技术和微信小程序平台开发的项目,主要应用于社区或公共卫生机构对居民健康状况的日常监控和管理。这个系统的构建利用了SpringBoot、SSM(Spring、SpringMVC、MyBatis)等...

    2024最新医院核酸检测预约挂号微信小程序.zip

    6. 用户认证与授权:为了保证用户信息安全,项目中可能实现了OAuth2.0或者JWT(JSON Web Token)等机制,用于用户身份验证和权限控制。 7. 异步处理和消息队列:考虑到高峰期可能出现的大流量,项目可能采用了消息...

Global site tag (gtag.js) - Google Analytics