SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken

微信小程序官方文档

https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html

登录流程

在登录时需要在小程序内部获取code,如何带上code发送给后端,后端带上appid+appsecret+code获取openid+session_key,拿到openid+session_key即可登录获取有用户信息。

  • 1.调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  • 2.调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台账号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台账号) 和 会话密钥 session_key。

之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

后端服务API

文档

https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html

登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。

请求参数

属性 类型 必填 说明

appid string 是 小程序 appId

secret string 是 小程序 appSecret

js_code string 是 登录时获取的 code,可通过wx.login获取

grant_type string 是 授权类型,此处只需填写 authorization_code

项目依赖

xml 复制代码
<!--胡图工具-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>

        <!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot-starter</artifactId>
            <version>1.39.0</version>
        </dependency>

<!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>  <!-- 只在开发环境下使用 -->
        </dependency>
        
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-redis-jackson</artifactId>
            <version>1.39.0</version>
        </dependency>

        <!-- 提供Redis连接池 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

微信小程序登录

配置文件

yml 复制代码
wx:
  appID: wxcfdadd0c4342e898da8d2
  appSecret: 6937ffc6fxda289aa64edd551b26c4021ed30

server:
  redis:
    # Redis数据库索引(默认为0)
    database: 1
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    # password:
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0
  # 端口
  port: 8080
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
  # token 名称(同时也是 cookie 名称)
  token-name: Authorization
  # token 有效期(单位:秒) 默认30天,-1 代表永久有效
  timeout: 432000
  # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
  active-timeout: 1200
  # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
  is-share: true
  # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
  token-style: random-128
  # 是否输出操作日志
  is-log: true

微信令牌配置类

java 复制代码
@Data
@Component
@ConfigurationProperties(prefix = "wx")
public class WxConfig {
    private String appID;
    private String appSecret;
}

Code验证

登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。

控制器
java 复制代码
private final IWxUserService iWxUserService;

@GetMapping("/getSession") //获取用户的唯一标识符 openId
public R getSession(String code){
    return  R.success("登录成功",iWxUserService.getSession(code));
}
实体类
java 复制代码
@Data
public class WxUser {
    private String nickName;
    private String gender;
    private String language;
    private String city;
    private String province;
    private String country;
    private String avatarUrl;
    private Long timestamp;
    private String phoneNumber;
    private String countryCode;
    private String token;
    @JsonIgnore  // 不序列化这个字段
    private String openId;
    @JsonIgnore  // 不序列化这个字段
    private String sessionKey;

}
业务层

openid表示一个用户,每个用户唯一的值,不会变化

java 复制代码
    private  final String WX_LOGIN_URL="https://api.weixin.qq.com/sns/jscode2session";
    private final WxConfig wxConfig;
    //微信登录URL
       @Override
    public WxUser getSession(String code) { //登录凭证校验接口  返回OPENID
        Map<String,String> query  =new HashMap<>();
        query.put("appid",wxConfig.getAppID()); //小程序 appId
        query.put("secret",wxConfig.getAppSecret());//小程序 appSecret
        query.put("js_code",code);//登录时获取的 code,可通过wx.login获取
        query.put("grant_type","authorization_code"); //授权类型,此处只需填写 authorization_code
        String url= Http.getSplice(WX_LOGIN_URL,query); //拼接URL
        String json =Http.get(url);
        JSONObject user = JSONUtil.parseObj(json);
        String openId=user.getStr("openid");
        String sessionKey=user.getStr("session_key");
        WxUser loginUser =new WxUser();

//        WxUser user= WxUserMapper.query(openId);
//        if(user==null) //新用户
//        {
//
//        }else{ //老用户
//
//
//        }

        loginUser.setOpenId(openId);
        loginUser.setSessionKey(sessionKey);

        StpUtil.login(openId); //用户登录
        StpUtil.getSession().set(WxLoginKey.SESSION_KEY,sessionKey ); //会话密钥 后面解密

        loginUser.setToken(StpUtil.getTokenValue());//设置token
       return loginUser;
    }
返回结果

新用户未授权只能拿到token,后续用户授权使用了信息可以保存数据库一起返回。

json 复制代码
{"code":200,"meg":"登录成功","data":{"nickName":null,"gender":null,"language":null,"city":null,"province":null,"country":null,"avatarUrl":null,"timestamp":null,"phoneNumber":null,"countryCode":null,"token":"HApeTHAHjo8uA8S3RJO2Z0QIibTiJoupcrcBE9ReeghTy09yI5zFlm9ztePspkNGmIVjylqubnDcm6r3CzKm8iqV8OFWz1cG9xeLslvFf4RbzSC42OqDr2W0XHjLFoVW"}}

手机号登录(获取)

控制器

java 复制代码
private final IWxUserService iWxUserService;

@PostMapping("/getPhone") //获取到用户的手机号
public R getSession(@RequestBody UserPhoneLoginDto userPhoneLoginDto){
    System.out.println(userPhoneLoginDto);
    WxUser user =iWxUserService.getUserPhoneMessage(userPhoneLoginDto);
    return R.success("操作成功",user);
}

请求实体类

前端需要传递的参数

java 复制代码
@Data
public class UserPhoneLoginDto {
    private String code;
    private String encryptedData;
    private String iv;
    private String sessionId;
}

业务层

java 复制代码
@Override
public WxUser getUserPhoneMessage(UserPhoneLoginDto userPhoneLoginDto) { //用户数据解密  手机号登录
    String sessionKey =  StpUtil.getSession().getString(WxLoginKey.SESSION_KEY);
    //获取session_key解密获取手机号
    WxUser user = WxUtils.phoneDecrypt(userPhoneLoginDto,sessionKey);
    return user;
}
json 复制代码
{"code":200,"meg":"操作成功","data":{"nickName":null,"gender":null,"language":null,"city":null,"province":null,"country":null,"avatarUrl":null,"timestamp":null,"phoneNumber":"122222222","countryCode":"86","token":null}}

HTTP请求工具类封装

java 复制代码
public class Http {
    public static String getSplice(String url,Map<String, String> params){ //优雅拼接GET请求
        UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(url);
        for (Map.Entry<String, String> entry : params.entrySet()) {
            uriBuilder.queryParam(entry.getKey(), entry.getValue());
        }
        return uriBuilder.toUriString();
    }
    public static String get(String url){
        return  HttpUtil.get(url);
    }
}

微信工具类

java 复制代码
public class WxUtils {


    public static WxUser phoneDecrypt(UserPhoneLoginDto userPhoneLoginDto, String tempSession) {
        //微信用户手机号解密

        String sessionKey=tempSession;

        byte[] encData =Base64.decodeBase64(userPhoneLoginDto.getEncryptedData());
        byte[] iv=Base64.decodeBase64(userPhoneLoginDto.getIv());
        byte[] key =Base64.decodeBase64(sessionKey);

        AlgorithmParameterSpec algorithmParameters =new IvParameterSpec(iv);

        try {
            Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding");

            SecretKeySpec keySpec =new SecretKeySpec(key,"AES");

            cipher.init(Cipher.DECRYPT_MODE,keySpec,algorithmParameters);

            String userData =new String(cipher.doFinal(encData),"UTF-8");

            System.out.println(userData);
            WxUser wxUser = JSONUtil.toBean(userData, WxUser.class);

        return wxUser;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (NoSuchPaddingException e) {
            throw new RuntimeException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        } catch (IllegalBlockSizeException e) {
            throw new RuntimeException(e);
        } catch (BadPaddingException e) {
            throw new RuntimeException(e);
        }
    }
}
相关推荐
NCIN EXPE3 小时前
redis 使用
数据库·redis·缓存
lUie INGA3 小时前
在2023idea中如何创建SpringBoot
java·spring boot·后端
hERS EOUS3 小时前
nginx 代理 redis
运维·redis·nginx
geBR OTTE3 小时前
SpringBoot中整合ONLYOFFICE在线编辑
java·spring boot·后端
Porunarufu3 小时前
博客系统UI自动化测试报告
java
NineData3 小时前
NineData 新增支持 GaussDB 到 StarRocks 实时数据复制能力
后端
sghuter4 小时前
数字资源分发架构解密
后端·架构·dubbo
小码哥_常4 小时前
Spring Boot启动慢?这5个优化点带你起飞
后端
NineData4 小时前
NineData将亮相DACon 2026上海站!解锁AGI时代数据“智理”新范式
数据库·后端·架构