微信扫码 授权码模式
授权码模式流程
1.客户端向授权服务器请求授权,包括客户端标识、重定向URI等信息。
2.授权服务器验证客户端身份,验证通过后,向客户端发放授权码。
3.客户端使用授权码向授权服务器请求Access Token。
4.授权服务器验证授权码的有效性,验证通过后,向客户端发放Access Token。
5.客户端使用Access Token向资源服务器请求资源。
6.资源服务器验证Access Token的有效性,验证通过后,向客户端返回资源。
1.准备工作
网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。 在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
2.请参考官方文档
https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
3.在前端
将微信登录二维码内嵌到自己页面
为了满足网站更定制化的需求,我们还提供了第二种获取code的方式,支持网站将微信登录二维码内嵌到自己页面中,用户使用微信扫码授权后通过JS将code返回给网站。 JS微信登录主要用途:网站希望用户在网站内就能完成登录,无需跳转到微信域下登录后再返回,提升微信登录的流畅性与成功率。 网站内嵌二维码微信登录JS实现办法:
步骤1:在页面中先引入如下JS文件(支持https):
http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
步骤2:在需要使用微信登录的地方实例以下JS对象:
var obj = new WxLogin({
self_redirect:true,
id:"login_container",
appid: "",
scope: "",
redirect_uri: "",
state: "",
style: "",
href: ""
});
打开前端保证有二维码出现
4.在后端
在启动类上引入,或者自己新建配置类
//远程调用第三方接口
@Bean
RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
return restTemplate;
}
pom引入依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
controller
@Slf4j
@Controller
public class WxLoginController {
@Autowired
WxAuthService wxAuthService;
@RequestMapping("/wxLogin")
public String wxLogin(String code, String state) throws IOException {
log.debug("微信扫码回调,code:{},state:{}", code, state);
//远程调用微信请令牌,拿到令牌查询用户信息,将用户信息写入本项目数据库
XcUser xcUser = wxAuthService.wxAuth(code);
if (xcUser == null) {
//返回错误的页面
return "redirect:http://www.51xuecheng.cn/error.html";
}
String username = xcUser.getUsername();
//http://www.51xuecheng.cn/sign.html改成自己需要跳转的页面,这里重定向登录页面
return "redirect:http://www.51xuecheng.cn/sign.html?username=" + username + "&authType=wx";
}
}
调用的业务层逻辑代码,自己改成自己的实体类和配置yml文件即可
@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService, WxAuthService {
@Autowired
XcUserMapper xcUserMapper;
@Autowired
XcUserRoleMapper xcUserRoleMapper;
@Autowired
WxAuthServiceImpl currentPorxy;
@Autowired
RestTemplate restTemplate;
@Value("${weixin.appid}")
String appid;
@Value("${weixin.secret}")
String secret;
@Override
public XcUser wxAuth(String code) {
//申请令牌
Map<String, String> access_token_map = getAccess_token(code);
//访问令牌
String access_token = access_token_map.get("access_token");
String openid = access_token_map.get("openid");
//携带令牌查询用户信息
Map<String, String> userinfo = getUserinfo(access_token, openid);
// 保存用户信息到数据库
XcUser xcUser = currentPorxy.addWxUser(userinfo);
return xcUser;
}
@Transactional
public XcUser addWxUser(Map<String,String> userInfo_map){
String unionid = userInfo_map.get("unionid");
String nickname = userInfo_map.get("nickname");
//根据unionid查询用户信息
XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getWxUnionid, unionid));
if(xcUser !=null){
return xcUser;
}
//向数据库新增记录
xcUser = new XcUser();
String userId= UUID.randomUUID().toString();
xcUser.setId(userId);//主键
xcUser.setUsername(unionid);
xcUser.setPassword(unionid);
xcUser.setWxUnionid(unionid);
xcUser.setNickname(nickname);
xcUser.setName(nickname);
xcUser.setUtype("101001");//学生类型
xcUser.setStatus("1");//用户状态
xcUser.setCreateTime(LocalDateTime.now());
//插入
int insert = xcUserMapper.insert(xcUser);
//向用户角色关系表新增记录
XcUserRole xcUserRole = new XcUserRole();
xcUserRole.setId(UUID.randomUUID().toString());
xcUserRole.setUserId(userId);
xcUserRole.setRoleId("17");//学生角色
xcUserRole.setCreateTime(LocalDateTime.now());
xcUserRoleMapper.insert(xcUserRole);
return xcUser;
}
/**
* 携带授权码申请令牌
* https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
*
* {
* "access_token":"ACCESS_TOKEN",
* "expires_in":7200,
* "refresh_token":"REFRESH_TOKEN",
* "openid":"OPENID",
* "scope":"SCOPE",
* "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
* }
* @param code 授权
* @return
*/
private Map<String,String> getAccess_token(String code){
String url_template = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
//最终的请求路径
String url = String.format(url_template, appid, secret, code);
//远程调用此url
ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, null, String.class);
//获取响应的结果
String result = exchange.getBody();
//将result转成map
Map<String,String> map = JSON.parseObject(result, Map.class);
return map;
}
/**
* 携带令牌查询用户信息
*
* https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
*
* {
* "openid":"OPENID",
* "nickname":"NICKNAME",
* "sex":1,
* "province":"PROVINCE",
* "city":"CITY",
* "country":"COUNTRY",
* "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
* "privilege":[
* "PRIVILEGE1",
* "PRIVILEGE2"
* ],
* "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
*
* }
* @param access_token
* @param openid
* @return
*/
private Map<String,String> getUserinfo(String access_token,String openid){
String url_template = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s";
String url = String.format(url_template, access_token, openid);
ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
//获取响应的结果
String result = new String(exchange.getBody().getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
//将result转成map
Map<String,String> map = JSON.parseObject(result, Map.class);
return map;
}
}
//接口
public interface WxAuthService {
/**
* 微信扫码认证,申请令牌,携带令牌查询用户信息、保存用户信息到数据库
* @param code 授权码
* @return
*/
public XcUser wxAuth(String code);
}
5.内网穿透测试,由于wx的接口需要返回响应给我们的服务,所以该模块必须暴露在公网,所以需要使用内网穿透
请参考
https://blog.csdn.net/qq_56533553/article/details/130173196
至此就可以调用第三方的接口获取到需要的用户信息了,注意这里只到了保存用户信息