admin管理员组文章数量:1539850
文章目录
- 一、需求:微信扫码登录
- 1、接口文档
- 2、开发环境准备
- 3、接入分析
- 4、接口定义
- 5、申请令牌
- 6、查询用户信息
- 7、保存用户信息
一、需求:微信扫码登录
(和第三方对接的流程
)
1、接口文档
找到第三方的接口文档【微信扫码登录】。可以看到:微信扫码登录是基于OAuth2.0的:
阅读文档,接入微信扫码登录的流程是:
- 给自己的网站申请AppID和AppSecret
- 获取授权码code
请求url:
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE
传参:
-
传入APPID等参数,访问获取code的地址,需要用户扫码确认授权【扫码登录】
-
用户允许授权后,重定向到redirect_uri,并带上授权码code和state参数
redirect_uri?code=CODE&state=STATE
- 用户拒绝授权,则不发生重定向
- 获取code还可以直接前端内嵌二维码到自己的网站,而不用跳转到微信域确认后再返回
//此时需要前端引入JS文件
http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
//然后将传参直接实例化到JS对象中
ar obj = new WxLogin({
self_redirect:true,
id:"login_container",
appid: "",
scope: "",
redirect_uri: "",
state: "",
style: "",
href: ""
});
- 拿着授权码请求另一个接口获取access_token
调用API:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
# 传参
appid: 应用唯一标识(申请)
secret: 应用密钥(申请)
code: 上一步的授权码
grant_type: authorization_code
#返回
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
- 调用接口获取用户信息
API: (GET)
https://api.weixin.qq/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
# 返回
{
openid: #普通用户的标识,对当前开发者帐号唯一
nickname: #普通用户昵称
sex: #普通用户性别,1为男性,2为女性
province: #普通用户个人资料填写的省份
city: #普通用户个人资料填写的城市
country: #国家,如中国为CN
headimgurl: #用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
privilege: #用户特权信息,json数组,如微信沃卡用户为(chinaunicom)
unionid: #用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的 unionid 是唯一的。
}
2、开发环境准备
- 注册账号 https://open.weixin.qq/
- 添加网站应用,输入网站外网域名作为微信回调域名
- 审核后拿到AppID和AppSecret
- 开发环境在内网,微信回调指向公网域名。想让微信回调请求到我们自己的内网开发计算机上,需要做
内网穿透
简单的说就是,外网不能直接访问内网的资源,可以让内网先和一个特定的外网IP建立隧道连接,这个外网IP再和其他的外网建立连接。这样就实现了外部IP能访问我们内网的环境
3、接入分析
时序图如下:
认证服务在这里需要做:
- 定义接口接收微信下发的授权码
- 收到授权码,调用微信接口申请令牌
- 收到令牌,调用微信接口获取用户信息
- 将用户信息写入自己网站的数据库(同时还有其他逻辑,如这个用户的角色也要set)
- 最后重定向到浏览器自动登录
4、接口定义
注意这里,前端代码中已经定义了,用户授权后的重定向地址。
=====>
根据这个地址定义WxLoginController类,接收授权码:
@Slf4j
@Controller
public class WxLoginController {
@RequestMapping("/wxLogin")
public String wxLogin(String code, String state) throws IOException {
log.debug("微信扫码回调,code:{},state:{}",code,state);
//请求微信申请令牌
//拿到令牌查询用户信息
//将用户信息写入本项目数据库
XcUser xcUser = new XcUser();
//暂时硬编写,目的是调试环境,看能不能跳到下面的地址
xcUser.setUsername("t1");
if(xcUser==null){
return "redirect:http://www.51xuecheng/error.html";
}
String username = xcUser.getUsername();
return "redirect:http://www.51xuecheng/sign.html?username="+username+"&authType=wx";
}
}
接Day9内容,一个认证接口,写不同的实现类。定义微信认证方式的实现类:
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @description 微信扫码认证
*/
@Slf4j
@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService {
@Autowired
XcUserMapper xcUserMapper;
@Override
public XcUserExt execute(AuthParamsDto authParamsDto) {
//账号
String username = authParamsDto.getUsername();
XcUser user = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, username));
if(user==null){
//返回空表示用户不存在
throw new RuntimeException("账号不存在");
}
XcUserExt xcUserExt = new XcUserExt();
BeanUtils.copyProperties(user,xcUserExt);
return xcUserExt;
}
}
中途测试一下,这时扫码授权后要能进入到/wxLogin接口,然后跳转到http://www.51xuecheng/sign.html?username=t1&authType=wx
边写边调,可以写死点数据,硬编码,最后统一测有问题修改代价太大。
5、申请令牌
请求第三方的服务,使用restTemplate,在启动类中配置RestTemple的bean
@Bean
RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
return restTemplate;
}
定义微信认证的service接口:
public interface WxAuthService {
public XcUser wxAuth(String code);
}
让之前的WxAuthServiceImpl类也实现WxAuthService 接口
@Slf4j
@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService, WxAuthService {
@Autowired
XcUserMapper xcUserMapper;
@Autowired
RestTemplate restTemplate; //注入远程调用的bean
@Value("${weixin.appid}") //appid和密钥都写在配置文件中
String appid;
@Value("${weixin.secret}") //使用value注解+刀乐儿大括号赋值
String secret;
public XcUser wxAuth(String code){
//收到code调用微信接口申请access_token
Map<String, String> access_token_map = getAccess_token(code);
if(access_token_map==null){
return null;
}
//获取用户信息
//添加用户到数据库
XcUser xcUser = null;
return xcUser;
}
/**
* 定义申请访问令牌的方法
* 响应示例:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
*/
private Map<String,String> getAccess_token(String code) {
String wxUrl_template = "https://api.weixin.qq/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
//请求微信地址
String wxUrl = String.format(wxUrl_template, appid, secret, code);
log.info("调用微信接口申请access_token, url:{}", wxUrl);
ResponseEntity<String> exchange = restTemplate.exchange(wxUrl, HttpMethod.POST, null, String.class);
String result = exchange.getBody();
log.info("调用微信接口申请access_token: 返回值:{}", result);
Map<String,String> resultMap = JSON.parseObject(result, Map.class);
return resultMap;
}
这种URL,可以现在String中用%s占位,后续再String.format方法传参,很像log.debug中的花括号{}占位
6、查询用户信息
private Map<String,String> getUserinfo(String access_token,String openid) {
String wxUrl_template = "https://api.weixin.qq/sns/userinfo?access_token=%s&openid=%s";
//请求微信地址
String wxUrl = String.format(wxUrl_template, access_token,openid);
log.info("调用微信接口申请access_token, url:{}", wxUrl);
ResponseEntity<String> exchange = restTemplate.exchange(wxUrl, HttpMethod.POST, null, String.class);
//防止乱码进行转码
String result = new String(exchange.getBody().getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
log.info("调用微信接口申请access_token: 返回值:{}", result);
Map<String,String> resultMap = JSON.parseObject(result, Map.class);
return resultMap;
}
7、保存用户信息
@Autowired
XcUserRoleMapper xcUserRoleMapper;
@Transactional
public XcUser addWxUser(Map userInfo_map){
String unionid = userInfo_map.get("unionid").toString();
//根据unionid查询数据库
XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getWxUnionid, unionid));
if(xcUser!=null){
return xcUser;
}
String userId = UUID.randomUUID().toString();
xcUser = new XcUser();
xcUser.setId(userId);
xcUser.setWxUnionid(unionid);
//记录从微信得到的昵称
xcUser.setNickname(userInfo_map.get("nickname").toString());
xcUser.setUserpic(userInfo_map.get("headimgurl").toString());
xcUser.setName(userInfo_map.get("nickname").toString());
xcUser.setUsername(unionid);
xcUser.setPassword(unionid);
xcUser.setUtype("101001");//学生类型
xcUser.setStatus("1");//用户状态
xcUser.setCreateTime(LocalDateTime.now());
xcUserMapper.insert(xcUser);
XcUserRole xcUserRole = new XcUserRole();
xcUserRole.setId(UUID.randomUUID().toString());
xcUserRole.setUserId(userId);
xcUserRole.setRoleId("17");//学生角色
xcUserRoleMapper.insert(xcUserRole);
return xcUser;
}
写完这些方法,最后完善wxAuth方法:
@Autowired
WxAuthServiceImpl currentProxy;
public XcUser wxAuth(String code){
//收到code调用微信接口申请access_token
Map<String, String> access_token_map = getAccess_token(code);
if(access_token_map==null){
return null;
}
System.out.println(access_token_map);
String openid = access_token_map.get("openid");
String access_token = access_token_map.get("access_token");
//拿access_token查询用户信息
Map<String, String> userinfo = getUserinfo(access_token, openid);
if(userinfo==null){
return null;
}
//将用户信息保存到数据库
XcUser xcUser = currentProxy.addWxUser(userinfo);
return xcUser;
}
最后完善/wxLogin接口
@Slf4j
@Controller //返回一个String,别RestController
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/error.html";
}
String username = xcUser.getUsername();
return "redirect:http://www.51xuecheng/sign.html?username="+username+"&authType=wx";
}
}
最后梳理下整个流程:
- 前端嵌入微信二维码到网站登录页
- 网页中实例化js对象,传入appid等参数 ,并指定授权后重定向后到Auth服务的/wxLogin入口
- /wxLogin接口收到的请求参数是授权码
- /wxLogin接口调用wxAuth方法
- 在wxAuth方法中,远程调用微信的API,申请令牌、查询用户、同步用户信息
- 最后接口return一个重定向地址,携带用户名和authType到我们网站的登录页面,走统一认证
- 到自定义的UserServiceImpl(UserDetailsService)中的loadUserByUsername,根据authType参数,走WxAuthServiceImpl中的execute方法
- WxAuthServiceImpl中的execute方法只校验现在数据库中有没有这个用户,有则返回,自动登录成功
版权声明:本文标题:【网课平台】Day10.对接第三方:实现微信扫码登录 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1725778322a1042037.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论