09实现微信扫码认证登陆

显示登陆二维码

微信OAuth2.0授权登录目前支持authorization_code模式,适用于拥有server端的应用授权,详情查看接口文档

微信开放平台注册身份并添加网站应用,添加应用时需要指定网站的外网域名作为用户授权后微信的回调域名,审核通过后查看生成的appID和AppSecret

在这里插入图片描述

内网穿透

如果我们的开发环境在局域网,而微信回调时需要指向一个公网域名,此时需要使用内网穿透技术让微信回调请求至我们的开发计算机上

内网穿透简单来说就是使用内网穿透服务器作为中介让外网可以间接访问内网获取数据

在这里插入图片描述

在本地电脑上安装内网穿透服务器的客户端如natapp,配置外网域名及其要访问的内网地址及端口,开启隧道之后会分配一个专属域名/端口

在这里插入图片描述

跳转二维码

第一步:跳转到微信域下生成的二维码页面

// 天机学堂
https://open.weixin.qq.com/connect/qrconnect?
appid=wx17655f8047b85150
// 重定向的网址
&redirect_uri=http://tjxt-user-t.itheima.net/api/auth/wxLogin
&response_type=code
&scope=snsapi_login
#wechat_redirect

// 我的谷粒
https://open.weixin.qq.com/connect/qrconnect?
appid=wxed9954c01bb89b47
// 重定向的网址
&redirect_uri=http://localhost:8160/auth/wxLogin
&response_type=code
&scope=snsapi_login
#wechat_redirect

第二步:用户允许授权后,将会重定向到指定的网址上并且带上code和state参数,若用户禁止授权则不会发生重定向

redirect_uri?code=CODE&state=STATE 

内嵌二维码

第一步: 在前端工程xc-ui-pc-static-portal目录下的wxsign.html页面中先引入wxLogin.js(支持https)文件http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js

  • 用户使用微信扫码授权后通过JS将code返回给网站,用户在网站内就能完成登录,无需跳转到微信域下登录后再返回,提升微信登录的流畅性与成功率
!function(e,t){
    e.WxLogin=function(e){
        var r="default";
        !0===e.self_redirect?r="true":!1===e.self_redirect&&(r="false");
        var n=t.createElement("iframe"),
            i="https://open.weixin.qq.com/connect/qrconnect?			appid="+e.appid+"&scope="+e.scope+"&redirect_uri="+e.redirect_uri+"&state="+e.state+"&login_type=jssdk&self_redirect="+r+"&styletype="+(e.styletype||"")+"&sizetype="+(e.sizetype||"")+"&bgcolor="+(e.bgcolor||"")+"&rst="+(e.rst||"");
        i+=e.style?"&style="+e.style:"",
        i+=e.href?"&href="+e.href:"",
        i+="en"===e.lang?"&lang=en":"",
        n.src=i,n.frameBorder="0",
        n.allowTransparency="true",
        n.scrolling="no",
        n.width="300px",
        n.height="400px";
        var s=t.getElementById(e.id);
        s.innerHTML="",
        s.appendChild(n)
    }
}(window,document);

第二步: 在需要使用微信登录的地方使用wxObj对象,此时页面上就会出现二维码了

// 我的谷粒
var wxObj = new WxLogin({
    self_redirect:true,
    id:"login_container", 
    appid: "wxed9954c01bb89b47", 
    scope: "snsapi_login", 
    redirect_uri: "http://localhost:8160/auth/wxLogin",// 扫描二维码授权后重定向到认证服务下的auth/wxLogin接口
    state: token,
    style: "",
    href: ""
});

// 天机学堂
function generateWxQrcode(token) {
    var wxObj = new WxLogin({
        self_redirect:true,
        id:"login_container", 
        appid: "wx17655f8047b85150", 
        scope: "snsapi_login", 
        redirect_uri: "http://tjxt-user-t.itheima.net/api/auth/wxLogin",// 扫描二维码授权后重定向到认证服务下的auth/wxLogin接口
        state: token,
        style: "",
        href: ""
    });

微信扫码认证

微信接口文档

访问接口获取用户信息,snsapi_base属于基础接口,若应用已拥有其它scope权限则默认拥有snsapi_base的权限,详情查看接口文档

  • snsapi_base可以让移动端网页授权时绕过跳转授权登录页请求用户授权的动作,直接跳转第三方网页带上授权临时票据code
  • snsapi_base会使得用户已授权作用域(scope)仅为snsapi_base,从而导致无法获取到需要用户授权才允许获得的数据和基础功能
接口作用域接口说明
snsapi_basehttps://api.weixin.qq.com/sns/oauth2/access_token通过 code 换取access token、refresh token和已授权scope
snsapi_basehttps://api.weixin.qq.com/sns/oauth2/refresh_token刷新或续期access_token使用
snsapi_basehttps://api.weixin.qq.com/sns/auth检access_token有效性
snsapi_userinfohttps://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID获取用户个人信息

在这里插入图片描述

接口请求/响应参数说明

参数是否必须说明
self_redirecttrue:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri
false(默认):手机点击确认登录后可以在 top window 跳转到 redirect_uri
id第三方页面显示二维码的容器id
appid应用唯一标识,在微信开放平台提交应用审核通过后获得
scope(应用授权作用域)如果拥有多个作用域使用逗号分隔,网页应用目前仅填写snsapi_login即可
redirect_uri重定向地址,需要进行UrlEncode
用户授权后重定向到redirect_uri的网址上并且带上code和state参数,若用户禁止授权则不会发生重定向
response_typecode
state用于保持请求和回调的状态,授权请求后原样带回给第三方
该参数可用于防止 csrf 攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加 session 进行校验
style提供black(默认)、white可选
href自定义样式链接,第三方可根据实际需求覆盖默认样式
code(授权码)用户授权后微信服务端返回给第三方应用的参数
grant_type微信认证:authorrization_code,密码模式: password
access_token第三方应用调用微信接口的凭证
expires_in接口调用凭证的超时时间(秒)
refresh_token用户刷新access_token
scope(用户授权作用域)用户授权作用域

重定向接收授权码

用户扫描二维码授权后会重定向到认证服务下的localhost:8160/auth/wxLogin接口并且带上code和state参数,这个重定向的地址需要在微信开放平台申请

第一步:在认证服务工程中定义接口接收微信下发的授权码,我们需要根据授权码远程调用微信的认证服务申请令牌,最后用令牌调用微信提供的获取用户信息接口

@Slf4j
@Controller// 因为要实现重定向这里只能使用@Controller
public class WxLoginController {
    @Autowired
    WxAuthServiceImpl 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();
        // 重定向到登陆页面自动登录,此时页面会访问的们的认证服务,认证逻辑是微信认证中实现的execute方法(判断数据库中是否有获取到的用户信息)
        return "redirect:http://www.51xuecheng.cn/sign.html?username="+username+"&authType=wx";
    }
}

第二步:在WxAuthServiceImpl重写wxAuth方法实现根据授权码远程调用微信认证服务申请令牌,拿到令牌后调用微信服务接口查询用户信息保存

public interface WxAuthService {
    XcUser wxAuth(String code);
}
@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService, WxAuthService {   
    // 注入代理对象,非事务方法调用事务方法要使用代理对象调用防止事务失效
    @Autowired
    WxAuthServiceImpl wxAuthService;

    @Override
    public XcUser wxAuth(String code) {
        // 携带授权码远程调用微信服务提供的接口获取微信服务颁发的令牌
        Map<String, String> access_token_map = getAccess_token(code);
        if(access_token_map==null){
            return null;
        }
        String accessToken = access_token_map.get("access_token");

        // 携带令牌和授权用户的唯一标识远程调用微信服务提供的接口查询用户信息
        String openid = access_token_map.get("openid");
        Map<String, String> user_info_map = getUserInfo(accessToken, openid);
        if(user_info_map == null){
            return null;
        }

        // 保存用户信息到本项目数据库(先查询如果有直接返回),这里需要使用代理对象调用
        XcUser xcUser = wxAuthService.addWxUser(user_info_map);
        return xcUser;  
    }
}

申请令牌

第一步: 在AuthApplication里注册RestTemplate到容器中,由于微信的认证服务属于对于第三方系统,所以需要使用RestTemplate发起请求申请令牌

@Bean
RestTemplate restTemplate(){
    RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    return  restTemplate;
}

第二步:在nacos中的auth-service-dev.yaml配置文件中配置认证服务的端口要与微信中配置的回调域名端口一致,配置申请的Appid和Appsecret

# 我的谷粒,http://localhost:8160/auth/wxLogin
server:
  servlet:
    context-path: /auth
  port: 8160
weixin:
  appid: wxed9954c01bb89b47
  secret: a7482517235173ddb4083788de60b90e

# 天机学堂,http://tjxt-user-t.itheima.net/api/auth/wxLogin
server:
  servlet:
    context-path: /auth
  port: 63070  
  
weixin: 
  appid:wx17655f8047b85150
  secret: 68918d65287802a19b1905cbda7eaa93  
  

第三步:在WxAuthServiceImpl类中定义申请令牌的私有方法,携带授权码,Appid,密钥远程调用微信服务提供的的sns/oauth2/access_token接口获取令牌

@Autowired
RestTemplate restTemplate;
@Value("${weixin.appid}")
String appid;
@Value("${weixin.secret}")
String secret;

private Map<String, String> getAccess_token(String code) {
    // 1. 请求路径模板,参数用%s占位符
    String url_template = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
    // 2. 填充占位符:appid,secret,code
    String url = String.format(url_template, appid, secret, code);
    // 3. POST方式远程调用URL,指定请求方式,请求内容,响应结果
    ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, null, String.class);
    // 4. 获取相应结果,响应结果为json格式的字符串
    String result = exchange.getBody();
    // 5. 将JSON数组转为map
    Map<String, String> map = JSON.parseObject(result, Map.class);
    return map;
}	

第四步: 查看响应的令牌信息是个JSON数组

// 申请访问令牌的成功响应示例
 {
 "access_token":"ACCESS_TOKEN",// 第三方应用调用微信接口的凭证
 "expires_in":7200,// 接口调用凭证的超时时间(秒)
 "refresh_token":"REFRESH_TOKEN",// 用户刷新access_token
 "openid":"OPENID",// 授权用户在微信服务上的唯一标识
 "scope":"SCOPE",// 用户授权作用域
 "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"// 如果我们在注册的微信平台账号上添加了多个网站,unionid是用户在我们这个注册账号上的唯一Id 
 }
// 错误的响应实例
{"errcode":40029,"errmsg":"invalid code"}

获取用户信息

第一步: 携带令牌调用查询用户信息接口/sns/userinfo,需要保证令牌有效且未超时并且微信用户已授权给第三方应用帐号相应接口作用域即可以调用哪些接口

private Map<String, String> getUserInfo(String access_token, String openid) {
    // 1. 请求路径模板,参数用%s占位符
    String url_template = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s";
    // 2. 填充占位符,access_token和openid
    String url = String.format(url_template, access_token, openid);
    // 3. GET方式远程调用URL
    ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
    // 4. 获取响应结果JSON格式的字符串,默认以ISO_8859_1格式编码
    String result = exchange.getBody();
    // 4.1 转码解决响应结果包含中文时的乱码问题
    result = new String(result.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
    // 5. 将JSON数组转为map
    Map<String, String> map = JSON.parseObject(result, Map.class);
    return map;
}

第二步: 查看微信服务端响应的用户信息JSON格式的字符串

{
    "openid":"OPENID",// 普通授权用户的标识,对当前开发者帐号唯一
    "nickname":"NICKNAME",// 普通用户昵称,如果昵称包含中文接收时需要转码
    "sex":1,// 普通用户性别,1为男性,2为女性
    "province":"PROVINCE",// 普通用户个人资料填写的省份
    "city":"CITY",// 普通用户个人资料填写的城市
    "country":"COUNTRY",
    // 用户头像,可以在浏览器直接访问,最后一个数值代表正方形头像大小,(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
    "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
    // 用户特权信息,如微信沃卡用户为chinaunicom
    "privilege":[
        "PRIVILEGE1",
        "PRIVILEGE2"
    ],
    // 用户统一标识,当前仅当网站应用已获得该用户的userinfo授权时,才会出现该字段
    "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}

保存用户信息

判断微信服务中查询到的用户在本项目用户数据库中是否存在,如果不存在则将其信息保存在本项目xc_users数据库的xc_user表中并返回

  • 保存用户信息时涉及到了多表操作所以需要进行事务控制, 另外非事务方法调用事务方法要使用代理对象调用防止事务失效
    在这里插入图片描述
    角色表
    在这里插入图片描述

用户角色关系表(多对多)

在这里插入图片描述

@Autowired
XcUserMapper xcUserMapper;
@Autowired
XcUserRoleMapper xcUserRoleMapper;
@Transactional
public XcUser addWxUser(Map<String, String> user_info_map){
    // 1. 获取用户的唯一标识unionid
    String unionid = user_info_map.get("unionid");
    // 2. 根据unionid判断用户表中是否存在该用户
    XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getWxUnionid, unionid));
    // 2.1 存在则直接返回
    if (xcUser != null){
        return xcUser;
    }
    // 2.2 不存在添加用户信息到用户表
    xcUser = new XcUser();
    // 2.3 设置主键
    String uuid = UUID.randomUUID().toString();
    xcUser.setId(uuid);
    // 2.4 设置其他数据库非空约束的属性
    xcUser.setUsername(unionid);
    xcUser.setPassword(unionid);
    xcUser.setWxUnionid(unionid);
    xcUser.setNickname(user_info_map.get("nickname"));// 用户昵称
    xcUser.setUserpic(user_info_map.get("headimgurl"));// 用户头像
    xcUser.setName(user_info_map.get("nickname"));
    xcUser.setUtype("101001");  // 用户类型,101001表示学生类型
    xcUser.setStatus("1");// 用户状态
    xcUser.setCreateTime(LocalDateTime.now());
    xcUserMapper.insert(xcUser);
    // 3.添加用户信息到用户角色关系表
    XcUserRole xcUserRole = new XcUserRole();
    xcUserRole.setId(uuid);
    xcUserRole.setUserId(uuid);
    xcUserRole.setRoleId("17");// 17表示学生角色
    xcUserRole.setCreateTime(LocalDateTime.now());
    xcUserRoleMapper.insert(xcUserRole);
    return xcUser;
}

重定向扫码认证校验

当执行完WxLoginController中的wxLogin方法后用户信息一定会被保存到本项目的用户数据库中

重定向本项目的认证服务并携带username=unionid&authType=wx进行扫码登陆,重写AuthServiceexecute方法实现微信登陆的验证逻辑

  • 判断本项目用户数据库中是否有该用户信息,有则登陆成功右上角显示登录用户名
@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService, WxAuthService {
    /**
    * 微信扫码认证,不需要校验密码和验证码,只需要判断用户是否存在
    * @param authParamsDto 认证参数
    * @return
    */
    @Override
    public XcUserExt execute(AuthParamsDto authParamsDto) {
        // 获取账号即unionid
        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;
    }
}
  • 37
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot框架是一种轻量级、开箱即用的Java开发框架,它大大简化了企业级应用的开发过程。要实现微信扫码登录,可以使用Spring Boot框架结合微信开放平台的API来实现。以下是实现微信扫码登录的步骤: 1. 注册微信开放平台账号并创建应用,获取AppID和AppSecret。 2. 创建Spring Boot项目并导入相关依赖,如web、httpclient等。 3. 在配置文件application.properties中配置微信开放平台的AppID和AppSecret。 4. 创建一个控制器,用于处理登录相关的请求。 5. 定义一个生成微信扫码登录链接的方法,该方法使用AppID、重定向URI和state等参数生成微信登录链接。 6. 在控制器中定义一个登录请求的接口,该接口返回生成的微信扫码登录链接。 7. 创建一个回调接口,用于处理微信登录成功后的回调请求。 8. 在回调接口中获取微信的授权code,通过code和AppID、AppSecret等参数向微信服务器发送请求,获取用户的唯一标识openid。 9. 将获取到的openid存储到数据库或Session中,表示用户已登录。 10. 在需要验证用户登录状态的接口中,通过openid验证用户是否已登录。 以上是使用Spring Boot实现微信扫码登录的基本步骤,通过控制器处理登录和回调接口,以及与微信服务器的交互,可以实现用户使用微信扫码登录系统的功能。当然,具体实现中还需要考虑安全性、数据持久化等问题,以及前端页面的设计和展示等方面的内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值