微信小程序与Java后端实现微信授权登录功能

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
全局流量管理 GTM,标准版 1个月
简介: 微信小程序极大地简化了登录注册流程。对于用户而言,仅仅需要点击授权按钮,便能够完成登录操作,无需经历繁琐的注册步骤以及输入账号密码等一系列复杂操作,这种便捷的登录方式极大地提升了用户的使用体验

前言

最近我和两个同学一起参加比赛,开发一个小程序,我担任队长一职,负责小程序的实现、后端开发以及 UI 设计,目前项目仍在开发中,感兴趣的话可以点击 Github 链接查看(给个Star),接下来我讲一下我如何使用UniappJava实现微信授权登录的😀

一、小程序中微信授权登录的优势

微信小程序极大地简化了登录注册流程。对于用户而言,仅仅需要点击授权按钮,便能够完成登录操作,无需经历繁琐的注册步骤以及输入账号密码等一系列复杂操作,这种便捷的登录方式极大地提升了用户的使用体验。

二、小程序登录流程

我来说一下这个流程图

  1. 小程序端通过调用wx.login()方法,获取零时登录凭证code,这个code是后面向 auth.code2Session微信服务器接口发送请求的重要参数
  2. 小程序使用wx.request()方法将获取到的code 发送到开发者服务器也就是后端
  3. 开发者服务器接收code,将开发者的appid、appsecret以及接收到的code发送到微信的 auth.code2Session接口服务,通过这个接口,开发者服务器可以获取session_key、openid等信息
  4. 开发者服务器可以将获取的的session_key、openid与自定义登录态进行关联,生成一个唯一的自定义登录态也就是token
  5. 开发者服务器将生成的自定义登录态返回给小程序
  6. 小程序接收到自定义登录态之后,将其存储入本地存储storage中,以便后续的业务请求使用
  7. 当小程序需要进行业务请求时,使用wx.request()方法发起请求,并携带存储在storage中的自定义登录态
  8. 开发者服务器接收到业务请求之后,通过自定义登录态查询对应的openid和session_key
  9. 返回业务数据

也就是说小程序调用wx.login()方法获取code,随后发送给后端服务器,后端接收到code之后将开发者的appid、appsecret以及接收到的code发送到微信auth.code2Session接口服务,微信接口服务返回openid、session_key关联生成token后返回给小程序,小程序存储本地storage,进行业务请求时携带token,后端接收后通过token查询对应的openid和session_key,最后返回业务数据

二、前端实现步骤

1.创建项目

使用npx degit dcloudio/uni-preset-vue#vite-ts wx-login-test命令

创建一个Vite+Vue3+TypescriptUniapp的项目

2.运行项目

使用 VSCode打开项目然后安装依赖

运行pnpm i安装依赖或者使用npm i

先将page/index/index.vue文件内容修改一下,内容如下:

<script setup lang="ts">
</script>
<template>
  <div>
    <button class="button">微信授权登录</button>
  </div>
</template>
<style scoped>
  .button {
    color: white;
    padding: 6rpx;
    background-color: #ED4556;
    border-radius: none;
  }
</style>

添加appid,修改manifest.json文件下的mp-weixinappid字段

appid从微信小程序后台获取

{
  // ...
  "mp-weixin": {
    // 这里为你的appid
    "appid": "your_appid",
    "setting": {
      "urlCheck": false
    },
    "usingComponents": true
  },
  // ...
}

运行pnpm dev:mp-weixin命令 启动项目

使用微信开发者工具运行根目录下的dist\dev\mp-weixin

效果如下

我们创建了一个登录按钮

3.获取Code

修改一下index.vue文件,内容如下:

<script setup lang="ts">
const onWxLogin = () => {
  uni.login({
    provider: 'weixin',
    success: async (res) => {
      const data = await uni.request({
        method: "POST",
        url: "http://localhost:8080/users/auth/wechat",
        data: {
          code: res.code
        }
      })
      console.log(data);
    },
  })
}
</script>
<template>
  <div>
    <button class="button" @click="onWxLogin">微信授权登录</button>
  </div>
</template>
<style scoped>
.button {
  color: white;
  padding: 6rpx;
  background-color: #ED4556;
  border-radius: none;
}
</style>

点击登录按钮会触发onWxLogin然后使用 uni.login()获取code,provider参数为登录服务提供商,在这里指定登录服务提供商为微信,在success成功回调中获取code,调用uni.request()发送给后端,后端调用微信服务接口获取openid、session_key,生成token

uni.login是一个客户端API,统一封装了各个平台的各种常见的登录方式,包括App手机号一键登陆、三方登录(微信、微博、QQ、Apple、google、facebook)、各家小程序内置登录

三、后端实现步骤

1.创建一个SpringBoot3项目

使用Idea创建或者在start.spring.io这个地址中创建

选择Spring WebMybatis FrameworkLombokMySQL Driver依赖

创建之后将application.properties配置文件后缀修改为application.properties

2.修改Maven配置文件pom.xml

pom.xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>login-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>login-server</name>
    <description>login-server</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>3.0.3</version>
            <scope>test</scope>
        </dependency>
        <!--httpclient的坐标用于在java中发起请求-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
        <!--使用fastjson解析json数据 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <!--java-jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>4.4.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3.新建数据库

新建一个数据库,然后新建一个users表用于存储用户信息

CREATE TABLE `users` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '用户主键ID',
  `open_id` varchar(255) NOT NULL COMMENT 'openId 微信用户唯一标识',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '微信用户' COMMENT '用户名',
  `avatar_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'https://thirdwx.qlogo.cn/mmopen/vi_32/POgEwh4mIHO4nibH0KlMECNjjGxQUq24ZEaGT4poC6icRiccVGKSyXwibcPq4BWmiaIGuG1icwxaQX6grC9VemZoJ8rg/132' COMMENT '用户头像',
  `create_time` datetime NOT NULL COMMENT '用户创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

usernamepassword指定默认的值

3.修改配置文件

然后再修改配置文件内容如下:

server:
  port: 8080 # 服务端口
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/your_database_name # 你的数据库名称
    username: your_database_username # 数据库用户名
    password: your_database_password # 数据库密码

上面的数据库名用户名密码要修改为你的

4.新建UserController

创建controller包,在下面创建UserController

5.添加微信授权登录接口

@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/users")
public class UserController {
    @PostMapping("/auth/wechat")
    public String authWechat() {
        return "登录成功";
    }
}

测试一下运行吧

点击按钮之后成功获取数据

6.封装Result统一返回类

先在创建一个utils软件包然后在下面创建一个Result类用于统一响应结果

package com.example.loginserver.utils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> implements Serializable {
    private Integer code;
    private String message;
    private T data;
    /**
     * 成功的结果响应 不带参数
     * @return
     * @param <T>
     */
    public static <T> Result<T> success() {
        return new Result<>(0, "success", null);
    }
        public static <T> Result<T> success(String message,T data) {
            return new Result<>(0, message, data);
        }
    /**
     * 成功的结果响应
     * @param data 响应数据
     * @return
     * @param <T>
     */
    public static <T> Result<T> success(T data) {
        return new Result<>(0, "success", data);
    }
    public static <T> Result<T> failure( String message) {
        return new Result<>(1, message, null);
    }
    /**
     * 错误的结果响应
     * @param code 状态码
     * @param message 错误消息
     * @return
     * @param <T>
     */
    public static <T> Result<T> failure(Integer code, String message) {
        return new Result<>(code, message, null);
    }
    /**
     * 错误的结果响应
     * @param code 状态码
     * @param message 错误消息
     * @param data 错误数据
     * @return
     * @param <T>
     */
    public static <T> Result<T> failure(Integer code, String message, T data) {
        return new Result<>(code, message, data);
    }
}

7.创建PODTO

创建POVO类,如果有同学分不清 DTO/VO/PO ,我可以简单介绍一下

  • DTO(Data Transfer Object):数据传输对象,前端传递给后端的数据
  • PO(Persistant Object):持久对象,属性与数据库表字段一一对应,用于插入数据库数据
  • VO(Value Object):值对象,也是用于数据传输的对象,后端返回给前端的,可以指定返回的字段

除了上面说的DTOPOVO之外,后面还有DOBO等概念可以自行上网了解这里就不说这么多了

Users 用于插入数据 与数据库表字段一一对应

@Data
@Builder
public class Users {
    /** 用户id */
    private Long id;
    /** 微信用户唯一标识 */
    private String openId;
    /** 用户名称 */
    private String username;
    /** 头像路径 */
    private String avatarUrl;
    /** 创建时间 */
    private LocalDateTime createTime;
}

WeChatDTO  用于接收前端传递的DTO

@Data
public class WeChatCodeDTO {
    /**微信token*/
    private String code;
}

8.创建ServiceMapper

  • controller - 控制层
  • service - 业务逻辑层
  • mapper - 数据访问层

UsersService

package com.example.loginserver.service;
public interface UsersService {}

UsersServiceImpl

@Service
@Slf4j
@RequiredArgsConstructor
public class UsersServiceImpl implements UsersService {}

UsersMapper

package com.example.loginserver.mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public class UsersMapper {}

9.实现接口

1.一切准备完毕,那就开始实现吧~😉

UsersController

@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/users")
public class UserController {
    private final UserService userService;
    /**
     * 小程序微信授权登录
     * @return
     */
    @PostMapping("/login/wechat")
    public Result<String> loginWithWeChat(@RequestBody WeChatCodeDTO weChatCodeDTO) {
        return userService.loginWithWeChat(weChatCodeDTO.getCode());
    }
}

UsersService

public interface UsersService {
    UserLoginVO loginWithWeChat(String code);
}

然后在UsersServiceImpl中实现

2.UsersServiceImpl实现

UsersServiceImpl

@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements UserService {
    private final WeChatProperties weChatProperties;
    private final UsersMapper usersMapper;
    private final String WECHAT_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";
    @Override
    public Result<String> loginWithWeChat(String code) {
        String openId = getOpenId(code);
        // 查询用户是否已存在
        Long userId = usersMapper.getUserByOpenId(openId);
        if (userId == null) {
            Users user = Users.builder().openId(openId).build();
            usersMapper.insertUsers(user);
            String token = JwtTokenUtil.generateTokenWithUserId(user.getId());
            return Result.success("登录成功", token);
        }
        return Result.success("登录成功", JwtTokenUtil.generateTokenWithUserId(userId));
    }
    public String getOpenId(String code) {
        // 封装请求参数
        HashMap<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");
        // 使用封装好的 HttpClientUtil 从微信后台请求
        String json = HttpClientUtil.doGet(WECHAT_LOGIN_URL, map);
        // 将获取过来的数据解析出来
        JSONObject jsonObject = JSON.parseObject(json);
        // 获取openId
        log.info("jsonObject:{}", jsonObject);
        return jsonObject.getString("openid");
    }
}

getOpenId这个方法是调用封装好的HttpClientUtil向微信auth.code2Session接口服务发起请求获取用户openid、session_key,这里我们返回openid

注意:appid需要跟小程序端的manifest.json文件中的appid一致,否者会报错

在authWechat这个方法中使用getOpenId获取openId,再使用openId查询用户是否已注册,调用封装好的jwt生成token,如果用户不存在则创建用户,并构建成UsersLoginVO对象返回给前端,如果已存在则直接构建成UsersLoginVO对象返回给前端

接下来我们在utils文件夹下新建JwtUtil和HttpClientUtil这两个工具类

JwtUtils

public class JwtTokenUtil {
    // 秘钥,实际应用中应该妥善保管,比如从配置文件读取等
    private static final String SECRET_KEY = "your_secret_key";
    // Token过期时间,这里设置为1小时,单位是毫秒,可以按需调整
    private static final long EXPIRATION_TIME = 60 * 60 * 1000;
    /**
     * 生成携带用户id的token
     * @param userId
     * @return
     */
    public static String generateTokenWithUserId(Long userId) {
        return JWT.create().withClaim("userId", userId).withExpiresAt(new Date(EXPIRATION_TIME)).sign(Algorithm.HMAC256(SECRET_KEY));
    }
    /**
     * 解析token并返回用户id
     * @param token
     * @return
     */
    public static String parseTokenGetUserId(String token) {
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(SECRET_KEY)).build().verify(token);
        return String.valueOf(decodedJWT.getClaim("userId"));
    }
}

HttpClientUtil

public class HttpClientUtil {
    private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
    private static final int TIMEOUT_MSEC = 5 * 1000;
    public static String doGet(String url, Map<String, String> paramMap) {
        String result = "";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        try {
            URIBuilder builder = new URIBuilder(url);
            if (paramMap != null) {
                for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                    builder.addParameter(entry.getKey(), entry.getValue());
                }
            }
            URI uri = builder.build();
            HttpGet httpGet = new HttpGet(uri);
            httpGet.setConfig(buildRequestConfig());
            response = httpClient.execute(httpGet);
            logger.info("GET Response status: {}", response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() == 200) {
                result = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
                logger.debug("GET Response body: {}", result);
            }
        } catch (Exception e) {
            logger.error("Error occurred while sending GET request", e);
        } finally {
            closeResources(response, httpClient);
        }
        return result;
    }
    public static String doPost(String url, Map<String, String> paramMap) {
        String resultString = "";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        try {
            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, StandardCharsets.UTF_8);
                httpPost.setEntity(entity);
            }
            httpPost.setConfig(buildRequestConfig());
            response = httpClient.execute(httpPost);
            logger.info("POST Response status: {}", response.getStatusLine().getStatusCode());
            resultString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            logger.debug("POST Response body: {}", resultString);
        } catch (Exception e) {
            logger.error("Error occurred while sending POST request", e);
        } finally {
            closeResources(response, httpClient);
        }
        return resultString;
    }
    public static RequestConfig buildRequestConfig() {
        return RequestConfig.custom()
            .setConnectTimeout(TIMEOUT_MSEC)
            .setConnectionRequestTimeout(TIMEOUT_MSEC)
            .setSocketTimeout(TIMEOUT_MSEC)
            .build();
    }
    private static void closeResources(CloseableHttpResponse response, CloseableHttpClient httpClient) {
        try {
            if (response != null) {
                response.close();
            }
            if (httpClient != null) {
                httpClient.close();
            }
        } catch (IOException e) {
            logger.error("Error occurred while closing resources", e);
        }
    }
}

3.UsersMapper

UsersMapper

package com.example.loginserver.mapper;
import com.example.loginserver.pojo.po.Users;
import com.example.loginserver.pojo.vo.UsersLoginVO;
import com.example.loginserver.pojo.vo.UsersLoginVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UsersMapper {
    // 根据 openid 查询用户是否存在
    @Select("select id from users where open_id = #{openId}")
    Long getUserByOpenId(String openId);
    // 新增用户 并返回id
    void insertUsers(Users users);
}

resource文件夹下新建mapper\UsersMapper.xml文件,代码如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.loginserver.mapper.UsersMapper">
    // 使用数据库自动生成的键
    // 指定将自动生成的键赋值给参数对象(Users)的id属性
    // 这样就能从Users对象中获取返回的id了
    <insert id="insertUsers" useGeneratedKeys="true" keyProperty="id">
        insert into users (open_id, create_time)
        values ( #{openId}, #{createTime})
    </insert>
</mapper>

4.添加拦截器

我们可以添加拦截器对token进行校验

@Component
@RequiredArgsConstructor
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
        // 1.获取请求的 url
        String url = request.getRequestURL().toString();
        // 2.判断请求的 url 中是否包含 login,如果包含,说明是登录操作,放行
        if (url.contains("login") ) {
            return true;
        }
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            System.out.println("OPTIONS 请求,放行");
            return true;
        }
        // 3.获取请求头中的 Authorization
        String token = request.getHeader("Authorization");
        log.info("请求头:{}", request.getHeader("Authorization"));
        // 4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
        if (!StringUtils.hasLength(token)) {
            response.setStatus(401);
            return false;
        }
        // 5.解析 token,如果解析失败,返回错误结果
        try {
            String userId = JwtTokenUtil.parseTokenGetUserId(token);
            request.setAttribute("userId", userId);
            return true;
        } catch (Exception e) {
            log.error("解析令牌失败", e);
            response.setStatus(401);
            return false;
        }
    }
}

request.setAttribut方便后续获取用户id

5.测试

接下来我们重启一下SpringBoot服务

再打开微信开发者工具

可以看到成功的返回了我们想要的结果用户idtoken

我们来看一下数据库有没有插入用户数据

🤓,新增了一条用户数据,usernamepassword我们自定了默认值,后面前端可以后端发送请求更新

扩展🔧

  • 前端获取到后端返回的用户id,token可以使用uni.setStorage进行本地存储
uni.setStorageSync('token', data.data.token)
  • 前端发送请求数据时从storage中获取token,添加到header请求头中发送给后端服务使用jwt进行校验
uni.request({
        // ...
        header: {
           Authorization:uni.getStorageSync('token')
        },
        // ...
})

总结📚

好啦!就写到这里吧!😃,相信你应该学会了如何实现微信小程序授权登录吧?前端小程序端实现很简单😏获取code发送给后端,获取到后端返回的自定义登录态之后使用uni.setStorageSync()进行存储,代码主要是后端处理,需要使用HttpClient向微信auth.codeSession接口服务发送请求获取openid,然后判断用户是否存在,如果不存在则创建新用户,存在则返回,生成自定义登录态token,后面前端发送请求获取数据是,校验一下请求头中的token即可

  • 微信小程序授权登录优势:微信小程序简化了登录注册流程,用户只需点击授权按钮即可完成登录,无需繁琐操作,提升了用户体验
  • 登录流程:小程序端获取code,后端接收code后,将appid、appsecret和code发送到微信auth.codeSession接口服务,获取openid、session_key,生成自定义登录态返回给小程序端,小程序端进行存储,后续发送请求时携带自定义登录态token,后端进行校验返回业务数据
  • 以及微信头像昵称的获取

参考资料:

好的谢谢同学们看到这里❤️

目录
打赏
0
12
12
1
19
分享
相关文章
|
1月前
|
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
199 3
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
244 5
微信小程序---授权登录
微信小程序---授权登录
166 0
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
121 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
后端开发中的性能优化策略
本文将探讨几种常见的后端性能优化策略,包括代码层面的优化、数据库查询优化、缓存机制的应用以及负载均衡的实现。通过这些方法,开发者可以显著提升系统的响应速度和处理能力,从而提供更好的用户体验。
122 6
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
112 2
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
261 3
后端开发中的缓存机制:深度解析与最佳实践####
本文深入探讨了后端开发中不可或缺的一环——缓存机制,旨在为读者提供一份详尽的指南,涵盖缓存的基本原理、常见类型(如内存缓存、磁盘缓存、分布式缓存等)、主流技术选型(Redis、Memcached、Ehcache等),以及在实际项目中如何根据业务需求设计并实施高效的缓存策略。不同于常规摘要的概述性质,本摘要直接点明文章将围绕“深度解析”与“最佳实践”两大核心展开,既适合初学者构建基础认知框架,也为有经验的开发者提供优化建议与实战技巧。 ####
深入理解后端开发:从基础到高级
本文将带你走进后端开发的神秘世界,从基础概念到高级应用,一步步揭示后端开发的全貌。我们将通过代码示例,让你更好地理解和掌握后端开发的核心技能。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供有价值的信息和启示。
175 6

热门文章

最新文章