SpringBoot后端实现小程序微信登录功能实现

 更新时间:2025年05月19日 15:22:18   作者:就叫年华吧丶  
微信小程序登录是开发者通过微信提供的身份验证机制,获取用户唯一标识(openid)和会话密钥(session_key)的过程,这篇文章给大家介绍SpringBoot后端实现小程序微信登录功能实现,感兴趣的朋友跟随小编一起看看吧

SpringBoot实现微信小程序登录简介

微信小程序登录是开发者通过微信提供的身份验证机制,获取用户唯一标识(openid)和会话密钥(session_key)的过程。SpringBoot作为后端框架,可以与小程序前端配合完成完整的登录流程。

小程序端调用wx.login()

小程序前端调用此API获取临时登录凭证code

示例代码:

wx.login({
  success(res) {
    if (res.code) {
      // 发送code到后端
    }
  }
})

SpringBoot后端处理登录

接收小程序传来的code

向微信接口服务发起请求验证code

获取用户唯一标识openid和会话密钥session_key

返回自定义登录态

后端生成自定义登录态(如token)并返回给小程序

小程序后续请求携带此登录态

SpringBoot后端实现微信登录

1.导入HttpClient的Maven坐标

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

2.Http工具类

/**
 * Http工具类
 */
public class HttpClientUtil {
​
    static final  int TIMEOUT_MSEC = 5 * 1000;
​
    /**
     * 发送GET方式请求
     * @param url
     * @param paramMap
     * @return
     */
    public static String doGet(String url,Map<String,String> paramMap){
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
​
        String result = "";
        CloseableHttpResponse response = null;
​
        try{
            URIBuilder builder = new URIBuilder(url);
            if(paramMap != null){
                for (String key : paramMap.keySet()) {
                    builder.addParameter(key,paramMap.get(key));
                }
            }
            URI uri = builder.build();
​
            //创建GET请求
            HttpGet httpGet = new HttpGet(uri);
​
            //发送请求
            response = httpClient.execute(httpGet);
​
            //判断响应状态
            if(response.getStatusLine().getStatusCode() == 200){
                result = EntityUtils.toString(response.getEntity(),"UTF-8");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                response.close();
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
        return result;
    }
​
    /**
     * 发送POST方式请求
     * @param url
     * @param paramMap
     * @return
     * @throws IOException
     */
    public static String doPost(String url, Map<String, String> paramMap) throws IOException {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
​
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
​
            // 创建参数列表
            if (paramMap != null) {
                List<NameValuePair> paramList = new ArrayList();
                for (Map.Entry<String, String> param : paramMap.entrySet()) {
                    paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
                }
                // 模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
                httpPost.setEntity(entity);
            }
​
            httpPost.setConfig(builderRequestConfig());
​
            // 执行http请求
            response = httpClient.execute(httpPost);
​
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
        return resultString;
    }
​
    /**
     * 发送POST方式请求
     * @param url
     * @param paramMap
     * @return
     * @throws IOException
     */
    public static String doPost4Json(String url, Map<String, String> paramMap) throws IOException {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
​
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
​
            if (paramMap != null) {
                //构造json格式数据
                JSONObject jsonObject = new JSONObject();
                for (Map.Entry<String, String> param : paramMap.entrySet()) {
                    jsonObject.put(param.getKey(),param.getValue());
                }
                StringEntity entity = new StringEntity(jsonObject.toString(),"utf-8");
                //设置请求编码
                entity.setContentEncoding("utf-8");
                //设置数据类型
                entity.setContentType("application/json");
                httpPost.setEntity(entity);
            }
​
            httpPost.setConfig(builderRequestConfig());
​
            // 执行http请求
            response = httpClient.execute(httpPost);
​
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
        return resultString;
    }
    private static RequestConfig builderRequestConfig() {
        return RequestConfig.custom()
                .setConnectTimeout(TIMEOUT_MSEC)
                .setConnectionRequestTimeout(TIMEOUT_MSEC)
                .setSocketTimeout(TIMEOUT_MSEC).build();
    }
​
}

3.JWT工具类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
​
public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     *
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息
     * @return
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
​
        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);
​
        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);
​
        return builder.compact();
    }
​
    /**
     * Token解密
     *
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }
​
}

4.准备好签名所需使用的密钥,过期时间等

sky:
  jwt:
    user-secret-key: itcast
    # 设置jwt过期时间
    user-ttl: 7200000
    # 设置前端传递过来的令牌名称
    user-token-name: authentication

5.用对象类将配置文件的属性封装

@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {
    /**
     * 用户端微信用户生成jwt令牌相关配置
     */
    private String userSecretKey;
    private long userTtl;
    private String userTokenName;
​
}

6.WeChatProperties对象类部分属性以及配置文件

关于怎么注册大家可以移步最下方。

@Component
@ConfigurationProperties(prefix = "sky.wechat")
@Data
public class WeChatProperties {
​
    private String appid; //小程序的appid
    private String secret; //小程序的秘钥
​
}
​
//application.yml配置文件
sky:
  wechat:
    appid: ${sky.wechat.appid}
    secret: ${sky.wechat.secret}
​
//application-dev.yml配置文件
sky:
  wechat:
#   TODO:需要在微信公众平台申请相关信息并填入
    appid: ******
    secret: ******

7.准备好DTO数据对象

/**
 * C端用户登录
 */
@Data
public class UserLoginDTO implements Serializable {
​
    private String code;
​
}

8.控制层接口

@RestController
@RequestMapping("/user/user")
@Slf4j
@RequiredArgsConstructor  // Lombok 自动生成构造函数
@Api(tags = "C端用户相关接口")
public class UserController {
    private final UserService userService;
    private final JwtProperties jwtProperties;
    /**
     * 微信登录
     * @param userLoginDTO
     * @return
     */
    @PostMapping("/login")
    @ApiOperation("微信登录")
    public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
        log.info("微信用户登录:{}",userLoginDTO.getCode());
​
        //微信登录
        User user = userService.wxLogin(userLoginDTO);
​
        //为微信用户生成jwt令牌
        Map<String,Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.USER_ID,user.getId());
        String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims);
​
        UserLoginVO userLoginVO = UserLoginVO.builder()
                .id(user.getId())
                .openid(user.getOpenid())
                .token(token)
                .build();
​
        return Result.success(userLoginVO);
    }
}

9.微信登录的服务层的接口和实现类

//接口
public interface UserService {
    /**
     * 微信登录接口
     * @param userLoginDTO
     * @return
     */
    User wxLogin(UserLoginDTO userLoginDTO);
}
​
//实现类
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
    @Autowired
    private WeChatProperties weChatProperties;
    @Autowired
    private UserMapper userMapper;
​
    /**
     * 微信登录
     * @param userLoginDTO
     * @return
     */
    @Override
    public User wxLogin(UserLoginDTO userLoginDTO) {
        String openid = getOpenid(userLoginDTO.getCode());
        //判断openid是否为空,如果为空表示登录失败,抛出业务异常
        if(openid == null){
            throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
        }
​
        //判断当前用户是否是新用户
        User user = userMapper.getByOpenid(openid);
​
        //如果是新用户,自动完成注册
        if(user == null){
            user = User.builder()
                    .openid(openid)
                    .createTime(LocalDateTime.now())
                    .build();
            userMapper.insert(user);
        }
        //返回这个用户对象
​
        return user;
    }
​
    /**
     * 调用微信接口服务,获取当前微信用户的openid
     * @param code
     * @return
     */
    private String getOpenid(String code){
        Map<String, String> map = new HashMap<>();
        map.put("appid",weChatProperties.getAppid());
        map.put("secret",weChatProperties.getSecret());
        map.put("js_code",code);
        map.put("grant_type","authorization_code");
        String json = HttpClientUtil.doGet(WX_LOGIN, map);
​
        JSONObject jsonObject =JSON.parseObject(json);
        String openid = jsonObject.getString("openid");
​
        return openid;
    }
}
​


注册小程序,获取key

微信公众平台

到此这篇关于SpringBoot后端实现小程序微信登录的文章就介绍到这了,更多相关SpringBoot小程序微信登录内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入探究Java线程不安全的原因与解决

    深入探究Java线程不安全的原因与解决

    线程不安全这个问题,一般在学Java时,我们老师会让我们背诵一段长长的话。"当不同线程同时能访问同一个变量时,可能会导致线程不安全"。实际上,这句话重点想突出的只有原子性。而我们往往考虑线程不安全的原因,会从三方面进行考虑:就是原子性,可见性,有序性
    2022-04-04
  • JAVA截取字符串的几种常用方法

    JAVA截取字符串的几种常用方法

    这篇文章主要给大家介绍了关于JAVA截取字符串的几种常用方法, 在处理字符串的过程中有很多情况下会遇到需要截取字符串的情况,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-09-09
  • 配置springboot项目使用外部tomcat过程解析

    配置springboot项目使用外部tomcat过程解析

    这篇文章主要介绍了配置springboot项目使用外部tomcat过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Hadoop的安装与环境搭建教程图解

    Hadoop的安装与环境搭建教程图解

    这篇文章主要介绍了Hadoop的安装与环境搭建教程图解,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-06-06
  • java 如何把byte转化为KB、MB、GB的方法

    java 如何把byte转化为KB、MB、GB的方法

    这篇文章主要介绍了java 如何把byte转化为KB、MB、GB的方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • 使用Java制作一个简单的记事本

    使用Java制作一个简单的记事本

    本文给大家带来的是使用Java制作一个简单的记事本的代码,有相同需要的朋友可以参考下
    2015-02-02
  • Java后端实现短链接生成功能

    Java后端实现短链接生成功能

    短链接生成主要在于把指定的接口和参数实现加密生成比较短的字符串,再进行拼接通过指定的域名或者ip实现链接的跳转,下面我们来看看如何使用Java实现这一功能吧
    2025-03-03
  • Spring配置与依赖注入基础详解

    Spring配置与依赖注入基础详解

    依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例
    2022-08-08
  • java条件语句示例详解

    java条件语句示例详解

    本文给大家介绍java条件语句,Java 中的条件语句允许程序根据条件的不同执行不同的代码块,一个 if 语句包含一个布尔表达式和一条或多条语句,本文结合示例代码给大家讲解的非常详细,需要的朋友可以参考下
    2023-05-05
  • SpringBoot基于自定义注解实现切面编程

    SpringBoot基于自定义注解实现切面编程

    这篇文章主要介绍了SpringBoot基于自定义注解实现切面编程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11

最新评论