GitHub:https://github.com/JDawnF
最近在做个项目是基于微信的,所以去查看了微信开发文档。
具体而言,网页授权流程分为四步:
1 第一步:用户同意授权,获取code
2 第二步:通过code换取网页授权access_token
3 第三步:刷新access_token(如果需要)
4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
5 附:检验授权凭证(access_token)是否有效
第一步:修改授权url:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 其中将appid,direct_uri需要换成自己公众号的;
direct_uri是授权后重定向的回调链接地址,请使用 urlEncode(转换,避免乱码,Java中是URLEncoder这个类) 对链接进行处理
scope:应用授权作用域,snsapi_base(不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 )
如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
code说明:code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
第二步:通过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获取用户信息等步骤,也必须从服务器发起。 获取code后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
正确返回json格式数据为:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
第三步:刷新access_token(如果需要)
由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。
获取第二步的refresh_token后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
第四步:拉取用户信息(需scope为 snsapi_userinfo)
如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。
请求方法:
http:GET(请使用https协议) https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
总而言之,就是先获取code,然后通过这个code去换取access_token,然后通过这个access_token就可以获取到用户的openId了。下面是基于Java的具体实现过程,有两种方式,第一种是手动获取openId,第二种是通过第三方sdk获取openId:
手动获取openId,将openid和SECRET换成自己对应的即可。:
@RestController
@RequestMapping("/weixin")
@Slf4j
public class WeiXinController {
//微信授权认证登录
@GetMapping("/auth")
public void auth(@RequestParam("code") String code) {
log.info("进入auth方法");
log.info("code={}", code);
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code="+ code + "&grant_type=authorization_code";
// 调用rest服务,面向的是资源而不是面向服务,都是对应HTTP方法
// 这里的getForObject()是发送一个HTTP GET请求,返回的请求体将映射为一个对象
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
log.info("response={}", response);
}
}
调用第三方sdk:
- 引入maven依赖
<!--sdk获取openid-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>2.7.0</version>
</dependency>
我这里用的springboot,所以需要在配置文件application.yml中加入appid和secret,然后再写一个类注入:
@Data
@Component
//注入application.yml中的属性,前缀为"wechat"
@ConfigurationProperties(prefix = "wechat")
public class WeChatAccountConfig {
/**
* 公众平台id
*/
private String mpAppId;
/**
* 公众平台密钥
*/
private String mpAppSecret;
}
然后再通过第三方sdk提供的方法获取这些属性:
@Component
public class WeChatMpConfig {
// 注入appid和AppSecret
@Autowired
private WeChatAccountConfig accountConfig;
// @Bean主要用于方法上,表示将这个id注入到spring容器中
@Bean
public WxMpService wxMpService() {
WxMpService wxMpService = new WxMpServiceImpl();
//获取注入的WxMpInMemoryConfigStorage的属性
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
@Bean
public WxMpConfigStorage wxMpConfigStorage() {
WxMpInMemoryConfigStorage wxMpConfigStorage = new WxMpInMemoryConfigStorage();
//获取注入的WeChatAccountConfig属性
wxMpConfigStorage.setAppId(accountConfig.getMpAppId());
wxMpConfigStorage.setSecret(accountConfig.getMpAppSecret());
return wxMpConfigStorage;
}
}
这里贴一下几个用的方法,看到就可以懂了
最后是controller实现:
@Controller
@RequestMapping("/wechat")
@Slf4j
public class WeChatController {
@Autowired
private WxMpService wxMpService;
@GetMapping("/authorize")
// 传入一个回调url,后面可以实现重定向
public String authorize(@RequestParam("returnUrl") String returnUrl) {
//WxMpService wxMpService = new WxMpServiceImpl();
//1.配置
//2.调用方法
// 外网ip
String url = 外网ip;
// URLEncoder.encode设置转码,防止url乱码
String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAUTH2_SCOPE_BASE,
URLEncoder.encode(returnUrl));
log.info("【微信网页授权】获取code,result={}", redirectUrl);
return "redirect:" + redirectUrl;
}
//获取code,并交换AccessToken
@GetMapping("/userInfo")
public String userInfo(@RequestParam("code") String code,
@RequestParam("state") String returnUrl) {
WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();
try {
wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
} catch (WxErrorException e) {
log.error("【微信网页授权】{}", e);
throw new SellException(ResultEnum.WECHAT_MP_ERROR.getCode(), e.getError().getErrorMsg());
}
String openId = wxMpOAuth2AccessToken.getOpenId();
return "redirect:" + returnUrl + "?openid=" + openId;
}
}
同样,贴出部分源码: