微信点餐系统

微信授权

一、获取OpenID(第一个关键点)

前期工作:
首先,你需要一个具有公网IP的域名
例如www.demo.com,它的域名解析的IP是123.123.123.123

在微信公众平台登录你的账号
微信公众平台:https://mp.weixin.qq.com

进入 开发->开发者工具 页面 
点击 公众平台测试账号 

由于我使用的是个人公众号(测试号),权限有限,需要在测试公众号配置。测试公众号有如下:
配置服务器和js接口安全域名

image
在第1 2 步时,需要对接口配置信息修改,在后台写一个接口用于验证微信发送的Token:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
private static String token = "root";
@GetMapping("/auth")
public String auth(@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr) {


boolean result = false;

// 对token、timestamp和nonce按字典序排序
String[] array = new String[]{token, timestamp, nonce};
Arrays.sort(array);

// 将三个参数字符拼接成一个字符串
String str = array[0].concat(array[1]).concat(array[2]);

String sha1Str = null;
try {
// 对拼接后的字符串进行sha1加密
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(str.getBytes());
sha1Str = byte2str(digest);
}
catch(Exception e) {
}

if(sha1Str != null && sha1Str.equals(signature)) {

return echostr;
}

return null;


}



public static String byte2str(byte[] array) {
StringBuffer hexstr = new StringBuffer();
String shaHex="";
for(int i = 0; i < array.length; i++) {
shaHex = Integer.toHexString(array[i] & 0xFF);
if(shaHex.length() < 2) {
hexstr.append(0);
}
hexstr.append(shaHex);
}
return hexstr.toString();
}
然后点击提提交

image
image

1、手工方式

(1) 第一步:用户同意授权,获取code

a文档说明:、在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,
修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;

授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 
http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com无法进行OAuth2.0鉴权

具体操作:公众账号-->公众号设置--->功能设置-->JS接口安全域名--->设置

image

在此的域名需要用到内网穿透工具(可以通过该域名访问自己的电脑),我用的是natapp(具体使用官网有介绍:需要下载客户端,配置文件,参考1分钟快速新手图文教程),
配置的过程中保证自己的127.0.0.1:8080能够访问,然后将MP_verify_xExFXcS84QAfvoeD.txt下载保存到项目的跟目录,我保存在resources/static下,
然后点确定(红框)。此时就可以通过http://wxorder.natapp1.cc/buyer/product/list访问项目了,这个域名wxorder.natapp1.cc就是回调域名了
    b、文档说明:在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_u
    serinfo),引导关注者打开如下页面(在微信中打开):

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=S
    TATE#wechat_redirect 若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。

    具体操作: https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx0807f8636ccc9325&redirect_uri=http://wxorder.natapp1.cc/sel
    l/weixin/auth&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect

    scope为snsapi_base只能获取到用户的openid,scope为snsapi_userinfo可以获取到更多的用户信息。

    c、下面定义一个接口,用于回调:
1
2
3
4
5
6
7
8
9
10
@RestController
@RequestMapping("/weixin")
@Slf4j
public class weixinController {

@GetMapping("/auth")
public void auth(@RequestParam("code")String code){
log.info("进入auth方法,code={}",code);
}
}
如果通过,则微信后台通过回调地址并带上code参数返回,通过接口可以获得code。例如:scope等于snsapi_userinfo时,如果用户同意授权,页面将跳转至 。(code说明 : code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。)
http://wxorder.natapp1.cc/sell/weixin/auth/?code=CODE&state=STATE。

(2) 第二步:通过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


具体:https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx0807f8636cc******&secret=3cce0fb303517513354b1255*****&code=CODE&grant_type=authorization_code
其中的code为上一步获取的code,这里的appid和secret为微信公众号上的(我使用的是测试号).

将项目中的接口改为如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/weixin")
@Slf4j
public class weixinController {

@GetMapping("/auth")
public void auth(@RequestParam("code")String code){
log.info("进入auth方法,code={}",code);

String url=":https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx0807f8636cc******&secret=3cce0fb303517513354b1255*****&code=CODE&grant_type=authorization_code";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url,String.class);
log.info("response={}",response);

}
}
重新在微信中点开步骤1中的链接,即可获得openid(response中获取,其信息是基于scope=snsapi_base)
正确时返回的JSON数据包如下:
{ "access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE" }


例如:
response={"access_token":"11_L1YOPXscF8****************KU24TxvUhIuPffmxQhfKHtuPMy7Mexg07-4vL6X1yq0SsJvb1LpCwJIELB8QBWFK****","expires_in":7200,"refresh_token":"11_zcxpq8U0Gc7biYxuF*****Mvm67NvmA96SYhYLwuWNkYeJ5Atx9WT3ptcq0ohY8vCHT2_tjCJgDVbij5xorap***","openid":"o97tn1TR6883y7GKzEiw***********","scope":"snsapi_base"}


(3) 第三步:刷新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

appid    是    公众号的唯一标识
grant_type    是    填写为refresh_token
refresh_token    是    填写通过access_token获取到的refresh_token参数
正确时返回的JSON数据包如下:
{ "access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE" }

(4) 第四步:拉取用户信息(需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

正确时返回的JSON数据包如下:
{    "openid":" OPENID",
" nickname": NICKNAME,
"sex":"1",
"province":"PROVINCE"
"city":"CITY",
"country":"COUNTRY",
"headimgurl":    "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
"privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}

详细说明参考微信开发者文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

(5) 附:检验授权凭证(access_token)是否有效

2、利用第三方SDK

@Controller
@RequestMapping("/wechat")
@Slf4j
public class WechatController {

@Autowired
private WxMpService wxMpService;

/**
 * 在通过调用http://wxorder.natapp1.cc/sell/wechat/authorize?returnUrl=http://www.imooc.com 时,会传一个returnUrl,用来重定向的网页地址
 * 通过将returnUrl、url(重定向到方法userInfo,获取opendi)传入方法oauth2buildAuthorizationUrl,该方法返回值为:
 * https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx0807f8636ccc9325&redirect_uri=http%3A%2F%2Fwxorder.natapp1.cc%2Fsell%2Fwechat%2FuserInfo&
 * response_type=code&scope=snsapi_userinfo&state=http%3A%2F%2Fwww.imooc.com#wechat_redirect
 * 然后重定向到userInfo,并传入参数code与state(其实就是传入的returnUrl),从而获取到了code参数
 * @param returnUrl
 * @return
 */
@RequestMapping("/authorize")
public String authorize(@RequestParam("returnUrl")String returnUrl) {

    //1、配置:基于类的配置

    //2、调用方法:该方法返回重定向的url,即url,并且带上了参数code与state
    String url = "http://wxorder.natapp1.cc/sell/wechat/userInfo";
    String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url,WxConsts.OAUTH2_SCOPE_USER_INFO, URLEncoder.encode(returnUrl));
    log.info("【微信网页授权】获取code,result={}",redirectUrl);
    return "redirect:"+redirectUrl;
}

/**
 * code参数传入方法oauth2getAccessToken 获取access_token,返回对象WxMpOAuth2AccessToken,该对象中封装了各种信息
 * @param code
 * @param returnUrl
 * @return
 */
@RequestMapping("/userInfo")
public String userInfo(@RequestParam("code") String code,
                     @RequestParam("state")String returnUrl){
    WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();
    try {
        wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
    } catch (WxErrorException e) {
        e.printStackTrace();
        log.error("【微信网页授权】{}",e);
        throw new SellException(ResultEums.WECHAT_ERROR.getCode(),e.getError().getErrorMsg());
    }
    String openid = wxMpOAuth2AccessToken.getOpenId();
    return "redirect:"+returnUrl+"?openid="+openid;


}
}

为了与接口配置信息时的请Url区分开,这里使用/wechat/authorize
对于authorize方法中对WxMpService的配置,将其提取处理单独配置,如下:
@Component
public class WechatMpConfig {

    @Autowired
    private WechatAccountConfig config;

    /**
     * 对WxMpService对象进行
     * @return
     */
    @Bean
    public WxMpService wxMpService() {
        WxMpService wxMpService = new WxMpServiceImpl();
        wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
        return wxMpService;
    }

    /**
     * 一个配置方法
     * @return
     */
    public WxMpConfigStorage wxMpConfigStorage() {
        WxMpInMemoryConfigStorage wxMpInMemoryConfigStorage = new WxMpInMemoryConfigStorage();
        wxMpInMemoryConfigStorage.setAppId(config.getMpAppId());
        wxMpInMemoryConfigStorage.setSecret(config.getMpAppSecret());

        return wxMpInMemoryConfigStorage;
    }
}

其中WechatAccountConfig为:
@Component
@Data
@ConfigurationProperties(prefix = "wechat")
public class WechatAccountConfig {
    private String mpAppId;
    private String mpAppSecret;

}

这是基于类的配置方式;属性mpAppId与属性mpAppSecret的配置在配置文件中如下:
wechat:
  mpAppId: wx0807f863**********
  mpAppSecret: 3cce0fb30351751335**************
文章目录
  1. 1. 一、获取OpenID(第一个关键点)
    1. 1.1. 1、手工方式
      1. 1.1.0.1. (1) 第一步:用户同意授权,获取code
      2. 1.1.0.2. (2) 第二步:通过code换取网页授权access_token
  2. 1.2. 2、利用第三方SDK