在做微信公众号开发时,经常需要对公众号上面的菜单做授权登录,如果是首次登录还需要做微信openId和系统账号的绑定操作。
这里做如下假设:
http://www.test.com
http://api.test.com
http://www.test.com/home/index
1、公众号注册以及实名认证
2、在“基本配置”中启用开发者密码(记得复制AppID、AppSecret
),并设置IP白名单
3、在“公众号设置-功能设置”中,配置“业务域名、JS接口安全域名、网页授权域名”
注意:上面几个操起需要将微信授权文件(MP_verify_xxxxx.txt
)下载存放在服务器上,需要让http://www.test.com/MP_verify_xxxxx.txt
可以访问。
假设把文件放在目录/usr/share/nginx/file
中,然后配置nginx
,让该链接可以访问,例如:
# 微信授权文件通用匹配规则
location ~(MP_verify_)*\.(txt)$ {
root /usr/share/nginx/file;
}
4、在“开发者工具-web开发者工具”中,绑定开发者微信号,便于在微信开发者工具中调试
这里推荐开源项目WxJava
在pom.xml中引入第三方jar包
<dependency>
<groupId>com.github.binarywanggroupId>
<artifactId>weixin-java-mpartifactId>
<version>4.5.0version>
dependency>
1)将复制下来的AppID、AppSecret
配置在application.yml
中
wx:
appId: wxd8e8db2818fxxxxx
appSecret: 4a22ab04b25eb155bd8b6a540cxxxxx
# 前端账号绑定页面url
bindUrl: http://www.test.com/wxBind
# 后端微信授权回调url
callback: http://api.test.com/wx/auth/callback
2)新建配置属性类WxProperties.java
@ConfigurationProperties(prefix = "wx")
@Data
public class WxProperties {
private String appId;
private String appSecret;
private String bindUrl;
private String callback;
}
3)新增配置类WxConfig.java
@Configuration
@EnableConfigurationProperties(WxProperties.class)
public class WxConfig {
private WxProperties wxProperties;
public WxConfig(WxProperties wxProperties) {
this.wxProperties = wxProperties;
}
@Bean
public WxMpService wxMpService(){
WxMpService wxMpService = new WxMpServiceImpl();
WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
configStorage.setAppId(wxProperties.getAppId());
configStorage.setSecret(wxProperties.getAppSecret());
wxMpService.setWxMpConfigStorage(configStorage);
return wxMpService;
}
}
1)新建微信授权回调接口Controller,编写授权回调接口
如果用户还未绑定,需要把openId传给前端,前端在绑定登录时,一起作为参数传回到后端的绑定接口
@Slf4j
@Controller
@RequestMapping("/wx/auth")
public class WxAuthController {
@Resource
private WxMpService wxMpService;
@Resource
private WxProperties wxProperties;
@RequestMapping("/callback")
public String callback(Model model, String state, String code) throws WxErrorException {
log.info("wx callback, code:{},state:{}", code, state);
WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code);
String openId = accessToken.getOpenId();
// 根据openId查找账号,如果不存在则新增绑定
String sysUserId = sysUserService.loadByWxOpenId(openId);
if (StringUtils.isNotBlank(sysUserId)) {
// 执行登录
LoginUser loginUser = sysUserService.getUserById(sysUserId);
// 跳转到前端页面地址
if (StringUtils.isNotBlank(state) && StringUtils.startsWith(state, "http")) {
// 生成登录Token,返回给前端
// 示例代码是通过Jwt生成Token,然后存储在Redis中
String token = saveToken(loginUser);
model.addAttribute("token", token);
log.info("公众号自动登录,userId:{},wxOpenId:{}", loginUser.getId(), openId);
return "redirect:" + state + "?token=" + token;
}
}
log.info("公众号未绑定,wxOpenId:{}", openId);
// 找不到已绑定记录,跳转到绑定页面,执行绑定
return "redirect:" + wxProperties.getBindUrl() + "?wxOpenId=" + openId + "&state=" + state;
}
// ... 暂时忽略其他方法 ....//
}
2)编写账号绑定接口
先校验账号密码,然后把微信openId和用户userId绑定,下次登录时候就可以根据openId查询到userId
@Data
public class WxBindVo {
@ApiModelProperty(value = "微信openId")
private String wxOpenId;
@ApiModelProperty(value = "账号")
private String username;
@ApiModelProperty(value = "密码")
private String password;
}
@RequestMapping("/bind")
@ResponseBody
public Result<JSONObject> bind(@RequestBody WxBindVo wxBindVo) throws WxErrorException {
log.info("wx bind, {}", wxBindVo);
Result<JSONObject> result = new Result<>();
JSONObject obj = new JSONObject();
// 执行绑定,然后自动登录,等完成后跳转到原来的页面
LoginUser loginUser = sysUserService.checkUser(wxBindVo.getUsername(), wxBindVo.getPassword());
if (loginUser != null) {
//绑定微信OpenId
sysUserService.saveWxAccount(loginUser.getId(), wxBindVo.getWxOpenId());
//用户登录信息
obj.put("userInfo", loginUser);
obj.put("token", saveToken(loginUser));
result.setResult(obj);
result.setSuccess(true);
result.setCode(200);
log.info("微信公众号绑定后自动登录,userId:{}, wxOpenId:{}", loginUser.getId(), wxBindVo.getWxOpenId());
return result;
}
result.setResult(obj);
result.setSuccess(false);
result.setMessage("公众号登录失败,请联系管理员");
return result;
}
3)编写微信公众号授权菜单入口
有了这个入口方法,只要将微信公众号菜单统一配置到这就可以,将需要打开的页面url
传给state
参数。
例如:
http://api.test.com/wx/auth/index?state=http://www.test.com/home/index
/**
* 微信公众号授权菜单入口
* @param state 授权登录跳转的页面url
*/
@RequestMapping("/index")
public String index(String state) {
String url = wxProperties.getCallback();
String authorizationUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_BASE, URIUtil.encodeURIComponent(state));
log.info("authorizationUrl = {}", authorizationUrl);
return "redirect:" + authorizationUrl;
}
当然,还有前端绑定页面开发,这里主要讲解流程,前端代码省略
打开公众号“内容于互动-自定义菜单”,添加添加菜单,输入菜单名单和跳转网页链接
这里网页链接假设是:
http://api.test.com/wx/auth/index?state=http://www.test.com/home/index