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

项目依赖

<!--胡图工具-->
        <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>

微信小程序登录

在这里插入图片描述

配置文件

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

微信令牌配置类

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

Code验证

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

控制器
private final IWxUserService iWxUserService;

@GetMapping("/getSession") //获取用户的唯一标识符 openId
public R getSession(String code){
    return  R.success("登录成功",iWxUserService.getSession(code));
}
实体类
@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表示一个用户,每个用户唯一的值,不会变化

    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,后续用户授权使用了信息可以保存数据库一起返回。

{"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"}}

手机号登录(获取)

控制器

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);
}

请求实体类

前端需要传递的参数

@Data
public class UserPhoneLoginDto {
    private String code;
    private String encryptedData;
    private String iv;
    private String sessionId;
}

业务层

@Override
public WxUser getUserPhoneMessage(UserPhoneLoginDto userPhoneLoginDto) { //用户数据解密  手机号登录
    String sessionKey =  StpUtil.getSession().getString(WxLoginKey.SESSION_KEY);
    //获取session_key解密获取手机号
    WxUser user = WxUtils.phoneDecrypt(userPhoneLoginDto,sessionKey);
    return user;
}
{"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请求工具类封装

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);
    }
}

微信工具类

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);
        }
    }
}
<think>好的,我现在需要帮助用户了解如何使用Java实现微信小程序登录接口。用户明确提到了需要教程,所以我的回答应该结构清晰,分步骤讲解。首先,我需要回忆微信小程序登录的流程,确保每个步骤正确无误。 用户提供的引用内容里有三个参考资料,虽然看起来和快速排序无关,但可能涉及到微信授权登录的具体实现。引用1提到了微信授权登录的优势和小程序与Java后端结合,引用2给出了Java处理code换取session_key和openid的示例代码,引用3则涉及获取用户手机号的方法。不过用户当前的问题集中在登录功能的实现,因此重点应放在登录流程上,手机号获取可能属于后续步骤,但可以简要提及。 首先,我需要整理微信小程序登录的标准流程。根据微信官方文档,登录流程通常包括:小程序端获取code,发送到后端后端用code、appid和appsecret向微信服务器请求session_key和openid,后端生成自定义登录态(如token)返回给小程序。这可能涉及到生成3rd_session来维护会话,但为了简化,可能直接使用JWT或其他方式。 接下来,我需要考虑如何将这些步骤转化为Java代码。用户提到使用Spring Boot框架,所以应该基于Spring Boot来构建示例。需要引入相关的依赖,比如weixin-java-mp,或者直接使用HttpClient发送HTTP请求到微信API。引用2中的示例使用了weixin-java-mp,这可能简化开发,但需要确保依赖正确。 然后,分步骤讲解: 1. 小程序端获取code:需要说明wx.login()的调用,并将code发送到后端接口。这部分需要简要说明,因为重点在后端。 2. 后端接收code并调用微信接口:需要创建Controller层,处理POST请求,接收code参数。然后使用code、appid和appsecret构造请求URL,发送到微信的API端点,获取session_key和openid。这里要注意参数的正确拼接,比如使用RestTemplate或OkHttp进行HTTP GET请求,并处理返回的JSON数据。 3. 生成自定义登录态:这里需要生成一个token,比如使用UUID或JWT,并将session_key和openid存储在Redis或数据库中,以便后续验证。需要提醒用户注意安全性,比如存储时的加密处理。 4. 返回token给小程序:将生成的token返回给前端,前端后续请求携带此token进行身份验证。 代码示例方面,需要给出关键的代码片段,如Controller方法、调用微信API的工具方法、生成token的方法。同时,需要指出需要替换的配置项,如appid和secret,以及可能的异常处理,比如code无效的情况。 可能还需要提到一些注意事项,比如安全方面的HTTPS使用、session_key的保密性,以及如何维护用户会话。此外,引用3中的获取手机号可以作为扩展功能,在用户登录后通过button触发,但需要用户主动授权,这里可以简要说明,但主要集中精力在登录流程本身。 最后,要确保回答结构清晰,步骤明确,代码示例正确,并且覆盖用户可能遇到的问题,比如依赖配置、参数错误处理等。同时,根据用户提供的引用,可能需要参考其中的代码结构,但需要调整以适应登录接口的具体需求。</think>以下是如何使用Java实现微信小程序登录接口的详细教程,分步骤说明关键流程和代码实现: ### 一、微信小程序登录流程原理 1. 小程序端调用`wx.login()`获取临时登录凭证code 2. 将code发送到Java后端服务器 3. 后端使用code+appid+secret向微信接口服务请求session_key和openid 4. 生成自定义登录态(3rd_session)并返回给小程序 5. 后续请求携带登录态进行身份验证 $$流程图示意:$$ 小程序端 → 后端服务器 → 微信接口服务 → 返回openid → 生成登录态 → 小程序存储 ### 二、Java后端实现步骤 #### 1. 添加必要依赖(pom.xml) ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> ``` #### 2. 创建登录接口Controller ```java @RestController @RequestMapping("/api/auth") public class WxLoginController { @Value("${wx.appid}") private String appid; @Value("${wx.secret}") private String secret; @PostMapping("/login") public ResponseEntity<?> wxLogin(@RequestBody Map<String,String> params) { String code = params.get("code"); // 调用微信接口获取session_key JSONObject wxResult = WxAuthUtil.getSessionKey(appid, secret, code); if(wxResult.containsKey("errcode")) { return ResponseEntity.badRequest().body("登录失败: "+wxResult.getString("errmsg")); } String openid = wxResult.getString("openid"); String sessionKey = wxResult.getString("session_key"); // 生成自定义登录态(示例使用UUID) String token = UUID.randomUUID().toString(); // 存储会话信息(示例使用RedisredisTemplate.opsForValue().set("SESSION:"+token, sessionKey + "#" + openid, 2, TimeUnit.HOURS); return ResponseEntity.ok(Collections.singletonMap("token", token)); } } ``` #### 3. 微信接口调用工具类 ```java public class WxAuthUtil { public static JSONObject getSessionKey(String appid, String secret, String code) { String url = "https://api.weixin.qq.com/sns/jscode2session?" + "appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code"; CloseableHttpClient client = HttpClients.createDefault(); HttpGet get = new HttpGet(url); try { HttpResponse response = client.execute(get); String result = EntityUtils.toString(response.getEntity()); return JSON.parseObject(result); } catch (IOException e) { throw new RuntimeException("微信接口调用失败", e); } } } ``` ### 三、配置参数(application.yml) ```yaml wx: appid: your_appid secret: your_appsecret ``` ### 四、小程序端对接示例 ```javascript // 小程序端调用示例 wx.login({ success(res) { if (res.code) { wx.request({ url: 'https://yourdomain.com/api/auth/login', method: 'POST', data: { code: res.code }, success(resp) { const token = resp.data.token wx.setStorageSync('token', token) } }) } } }) ``` ### 关键注意事项 1. **安全存储配置**:appid和secret必须通过配置文件管理,禁止硬编码 2. **HTTPS要求**:所有接口必须部署在HTTPS环境 3. **会话管理**:建议使用Redis存储session_key,设置合理过期时间 4. **敏感数据**:session_key不可返回给前端,需妥善保管[^2] 5. **错误处理**:需要处理微信接口返回的errcode(如40029无效code) ### 扩展功能(获取手机号) 参考微信官方流程,需要用户主动触发授权按钮: ```java // 根据引用[3]的代码示例扩展 public String getPhoneNumber(String code, String token) { // 通过微信接口获取手机号 // 注意:需要先获取access_token } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生产队的驴.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值