【案例实战】分布式应用下登录检验解决方案(JWT)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 【案例实战】分布式应用下登录检验解决方案(JWT)

1.需求背景以及JWT简介

  • 现实场景中,有些功能是需要登录才能访问的,比如购物车,个人订单等等。登录功能是最常见的功能。

(1)什么是JWT

JWT 是一个开放标准,它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。

简单来说: 就是通过一定规范来生成token,然后可以通过解密算法逆向解密token,这样就可以获取用户信息

  • 优点
  • 生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库
  • 存储在客户端,不占用服务端的内存资源
  • 缺点
  • token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,如用户权限,密码等
  • 如果没有服务端存储,则不能做登录失效处理,除非服务端改秘钥
  • JWT格式组成 头部、负载、签名
  • header+payload+signature
  • 头部:主要是描述签名算法
  • 负载:主要描述是加密对象的信息,如用户的id等,也可以加些规范里面的东西,如iss签发者,exp 过期时间,sub 面向的用户

  • 签名:主要是把前面两部分进行加密,防止别人拿到token进行base解密后篡改token
  • 关于jwt客户端存储
  • 可以存储在cookie,localstorage和sessionStorage里面

(2)用户登录流程图


af2ff0f49d644496854d307ec800f7bd.jpg

2.创建Maven项目,搭建SpringBoot项目

(1)添加maven依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

(2)创建yml配置文件

server:
  port: 8001
spring:
  application:
    name: login-server

(3)创建运行主类

@SpringBootApplication
public class LoginApplication {
    public static void main(String[] args) {
        SpringApplication.run(LoginApplication.class, args);
    }
}

3.容器化急速部署MySQL

(1)创建目录

mkdir -p /usr/local/docker/mysql/conf
mkdir -p /usr/local/docker/mysql/logs
mkdir -p /usr/local/docker/mysql/data

(2)容器启动mysql服务

docker run -p 3306:3306 --name mysql \ 
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:8.0
#查看容器
docker ps

97fe5c2a80784527b458b9a7aa4d9896.jpg

(3)可视化工具连接


9ab769b06cc44cecbfd95ac36876cd08.jpg

4.数据库表准备

(1)创建数据库user库


a4ed3c9fbef5495baccc9038167d47f4.jpg

(2)创建测试用户表


851752656b4a4402befa080fe4c15afd.jpg

  • 创建表sql脚本
/*
 Navicat Premium Data Transfer
 Source Server         : mysql_test
 Source Server Type    : MySQL
 Source Server Version : 80027
 Source Host           : 192.168.139.100:3306
 Source Schema         : user
 Target Server Type    : MySQL
 Target Server Version : 80027
 File Encoding         : 65001
 Date: 15/11/2022 09:14:11
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(0) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `username` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '用户名',
  `password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '密码',
  `phone` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '手机号',
  `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '用户姓名',
  `sex` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '用户性别',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `age` int(0) NULL DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;

(3)创建测试登录用户

  • **注意:**这里只是做演示,正常企业密码不会设置明文的,按照企业自己的加密方式去加密密码,我们现在主要是为了开发登录鉴权这一套流程。
INSERT INTO `user`.`user`(`id`, `username`, `password`, `phone`, `name`, `sex`, `create_time`, `age`) VALUES (1, 'lixiang', '1234567890', '13830567835', '李祥', '男', '2022-11-15 09:19:26', 18);

5.SpringBoot整合MySQL+MyBatisPlus

(1)添加maven依赖

        <!--mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.18</version>
        </dependency>

(2)配置yml文件

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.139.100:3306/user?allowPublicKeyRetrieval=true&characterEncoding=UTF-8&allowMultiQueries=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 8
      max-active: 20
      max-wait: 60000
      min-evictable-idle-time-millis: 30000

(3)启动主类添加MapperScan()注解

@MapperScan("com.lixiang.mapper")

(4)启动验证

f27fec39fe014b6fa3d88312346287ae.jpg

6.MyBatisPlus逆向工程自动生成

(1)加入maven依赖

        <!-- 代码自动生成依赖 begin -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!-- velocity -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <!-- 代码自动生成依赖 end-->

(2)运行代码

package com.lixiang.db;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
/**
 * @description mybatis自定生成工具
 */
public class MyBatisPlusGenerator {
    public static void main(String[] args) {
        //1. 全局配置
        GlobalConfig config = new GlobalConfig();
        // 是否支持AR模式
        config.setActiveRecord(true)
                // 作者
                .setAuthor("lixiang")
                // 生成路径,最好使用绝对路径,window路径是不一样的
                //TODO  TODO  TODO  TODO
                .setOutputDir("D:\\IDEAWork\\springboot-login\\src\\test\\java")
                // 文件覆盖
                .setFileOverride(true)
                // 主键策略
                .setIdType(IdType.AUTO)
                .setDateType(DateType.ONLY_DATE)
                // 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
                .setServiceName("%sService")
                //实体类结尾名称
                .setEntityName("%sDO")
                //生成基本的resultMap
                .setBaseResultMap(true)
                //不使用AR模式
                .setActiveRecord(false)
                //生成基本的SQL片段
                .setBaseColumnList(true);
        //2. 数据源配置
        DataSourceConfig dsConfig = new DataSourceConfig();
        // 设置数据库类型
        dsConfig.setDbType(DbType.MYSQL)
                .setDriverName("com.mysql.cj.jdbc.Driver")
                //TODO 修改数据库对应的配置
                .setUrl("jdbc:mysql://121.36.81.39:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai")
                .setUsername("root")
                .setPassword("123456");
        //3. 策略配置globalConfiguration中
        StrategyConfig stConfig = new StrategyConfig();
        //全局大写命名
        stConfig.setCapitalMode(true)
                // 数据库表映射到实体的命名策略
                .setNaming(NamingStrategy.underline_to_camel)
                //使用lombok
                .setEntityLombokModel(true)
                //使用RestController注解
                .setRestControllerStyle(true)
                // 生成的表, 支持多表一起生成,以数组形式填写
                //TODO  TODO  TODO  TODO
                .setInclude("user");
        //4. 包名策略配置
        PackageConfig pkConfig = new PackageConfig();
        pkConfig.setParent("com.lixiang")
                .setMapper("mapper")
                .setService("service")
                .setController("controller")
                .setEntity("model")
                .setXml("mapper");
        //5. 整合配置
        AutoGenerator ag = new AutoGenerator();
        ag.setGlobalConfig(config)
                .setDataSource(dsConfig)
                .setStrategy(stConfig)
                .setPackageInfo(pkConfig);
        //6. 执行操作
        ag.execute();
        System.out.println("=======  相关代码生成完毕  ========");
    }
}

0b4fe09f6e514563a61e6214155927ba.jpg198419d21a0648c38427b65bb81547af.jpgcfcd8ee0cfd94c1a8a9d5f0041d45507.jpg

(3)启动验证

76845278b02f4ede9eb7ee7e56bdbaea.jpg

7.SpringBoot整合JWT

(1)添加maven依赖

            <!-- JWT相关 -->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.7.0</version>
            </dependency>

(2)编写登录用户类

/**
 * 登录user实体bean
 * @author lixiang
 * @since 2022-01-13
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser {
    /**
     * 主键
     */
    private Long id;
    /**
     * 用户名
     */
    private String username;
    /**
     * 姓名
     */
    private String name;
    /**
     * 手机号
     */
    private String phone;
    /**
     * 用户性别
     */
    private String sex;
    /**
     * 年龄
     */
    private Integer age;
}

(3)编写JWTUtil

/**
 * JWT工具类
 * @author lixiang
 * @since 2022-01-13
 */
@Slf4j
public class JWTUtil {
    /**
     * token过期时间,正常是7天
     */
    private static final long EXPIRE = 1000L * 60 * 60 * 24 * 7;
    /**
     * 加密的密钥
     */
    private static final String SECRET = "lixiang.com";
    /**
     * 令牌前缀
     */
    private static final String TOKEN_PREFIX = "LONGIN-TEST";
    /**
     * subject 颁布地址
     */
    private static final String SUBJECT = "lixiang";
    /**
     * 根据用户信息生成token
     * @param loginUser
     * @return
     */
    public static Map<String,Object> geneJsonWebToken(LoginUser loginUser){
        if(loginUser == null){
            throw new NullPointerException("loginUser对象为空");
        }
        Date endDate = new Date(System.currentTimeMillis() + EXPIRE);
        String token = Jwts.builder().setSubject(SUBJECT)
                .claim("age",loginUser.getAge())
                .claim("id",loginUser.getId())
                .claim("name",loginUser.getName())
                .claim("phone",loginUser.getPhone())
                .claim("sex",loginUser.getSex())
                .setIssuedAt(new Date())
                .setExpiration(endDate)
                .signWith(SignatureAlgorithm.HS256,SECRET)
                .compact();
        token = TOKEN_PREFIX+token;
        Map<String,Object> map = new HashMap<>();
        map.put("accessToken",token);
        map.put("accessTokenExpires",endDate);
        return map;
    }
    /**
     * 检验token方法
     * @param token
     * @return
     */
    public static Claims checkJWT(String token){
        try{
            return Jwts.parser().setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                    .getBody();
        }catch (Exception e){
            log.info("JWT token解密失败");
            return null;
        }
    }
}

(4)测试JWT,编写测试方法

public class Main {
    public static void main(String[] args) {
        LoginUser loginUser = LoginUser.builder()
                .age(18)
                .id(1L)
                .name("李祥")
                .phone("13820934720")
                .sex("男")
                .username("lixiang")
                .build();
        Map<String, Object> objectMap = JWTUtil.geneJsonWebToken(loginUser);
        System.out.println("LoginUser加密:");
        objectMap.forEach((k,v)->{
            System.out.println("---key:"+k+",value:"+v);
        });
        String accessToken = String.valueOf(objectMap.get("accessToken"));
        System.out.println("Token解密:");
        Claims claims = JWTUtil.checkJWT(accessToken);
        System.out.println("---name:"+claims.get("name"));
        System.out.println("---age:"+claims.get("age"));
        System.out.println("---phone:"+claims.get("phone"));
        System.out.println("---username:"+claims.get("username"));
        System.out.println("---sex:"+claims.get("sex"));
    }
}

dae62bde08cc4d8aba3a27f3e660ea9b.jpg

8.开发测试接口

  • 开发两个接口,我们的目的是一个用于不需要登录就能访问,一个需要登录才能访问
  • 查看商品信息列表,查看个人订单信息两个接口
  • 开发测试的UserController,这块全部用的测试接口,主要是给大家演示效果,现在我们想让商品列表的接口可以随便访问,订单列表的接口只有用户登录之后才能访问。
/**
 * @description 测试Controller
 * @author lixiang
 */
@RestController
@RequestMapping("/user")
public class UserController {
    /**
     * 查询商品列表
     * @return
     */
    @GetMapping("/product_list")
    public Object getProductList(){
        return getResult(200,"查询商品列表");
    }
    /**
     * 查询订单列表
     * @return
     */
    @GetMapping("/order_list")
    public Object getOrderList(){
        return getResult(200,"查询订单列表");
    }
    /**
     * 测试返回结果
     * @param code
     * @param msg
     * @return
     */
    private Object getResult(int code, String msg) {
        Map<String,Object> result = new HashMap<>();
        result.put("code",code);
        result.put("msg",msg);
        return result;
    }
}
  • 测试


86e641d556eb4b0fb35119654679d552.jpg



887a89fd7e3544a483c3af9647b17a2c.jpg

  • 两个接口访问正常,但是对于订单接口我们是想增加Token检验,才会给予访问,这块我们就需要写一个拦截器,但是我们现在应该先去开发一下登录的接口。

9.开发登录接口

  • 这块我们采用手机号和密码登录

(1)创建登录请求类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginReq {
    /**
     * 手机号
     */
    private String phone;
    /**
     * 密码
     */
    private String password;
}

(2)创建login方法在UserServie

public interface UserService {
    /**
     * 登录方法
     * @param req
     * @return
     */
    Map<String, Object> login(LoginReq req);
}

(3)login实现类编写

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public Map<String, Object> login(LoginReq req) {
        UserDO user = userMapper.selectOne(new QueryWrapper<UserDO>().eq("phone", req.getPhone()));
        Map<String,Object> result = new HashMap<>();
        //判断是否已经注册的
        if (user == null) {
            //未注册
            result.put("code",10000);
            result.put("msg","用户未注册");
            return result;
        }
        if (req.getPassword().equals(user.getPassword())) {
            //登录成功,生成token,UUID生成token,存储到redis中并设置过期时间
            LoginUser loginUser = LoginUser.builder().build();
            BeanUtils.copyProperties(user, loginUser);
            return JWTUtil.geneJsonWebToken(loginUser);
        }
        result.put("code",10000);
        result.put("msg","密码错误");
        return result;
    }
}

(4)编写Controller

    @Autowired
    private UserService userService;
    /**
     * 登录
     * @return
     */
    @PostMapping("/login")
    public Object login(@RequestBody LoginReq req){
        return userService.login(req);
    }

(5)测试登录接口


571c9949754f4fa6ab31e2594ffa2c0f.jpg

10.开发登录拦截器

  • 登录接口开发完成了,那么我们需要开发一个登录拦截器。
/**
 * 全局登录拦截器
 * @author lixiang
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //从请求头中拿token
        String accessToken = request.getHeader("token");
        //从请求参数中拿token
        if (accessToken == null){
            accessToken = request.getParameter("token");
        }
        if(accessToken!=null && !accessToken.equals("")){
            //不为空,判断是否登录过期
            Claims claims = JWTUtil.checkJWT(accessToken);
            if (claims == null){
                sendJsonMessage(response, "账号已过期");
                return false;
            }
            Long userId = Long.valueOf(claims.get("id").toString());
            String headImg = (String) claims.get("username");
            String name = (String) claims.get("name");
            String phone = (String) claims.get("phone");
            String sex = (String) claims.get("sex");
            Integer age = (Integer) claims.get("age");
            //设置LoginUser对象属性,建造者模式
            LoginUser loginUser = LoginUser.builder()
                    .name(name)
                    .username(headImg)
                    .id(userId)
                    .phone(phone)
                    .sex(sex)
                    .age(age).build();
            //通过threadLocal共享用户登录信息
            threadLocal.set(loginUser);
            return true;
        }
        sendJsonMessage(response, "账号未登录");
        return false;
    }
    /**
     *
     * @param response
     * @param msg
     */
    private void sendJsonMessage(HttpServletResponse response, String msg) {
        Map<String,Object> result = new HashMap<>();
        result.put("code",10000);
        result.put("msg",msg);
        ObjectMapper objectMapper = new ObjectMapper();
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.print(objectMapper.writeValueAsString(result));
            response.flushBuffer();
        } catch (IOException e) {
            log.warn("响应json数据给前端异常");
        }finally {
            if(writer!=null){
                writer.close();
            }
        }
    }
}

(2)配置接口拦截

/**
 * 登录拦截配置类
 * @author lixiang
 */
@Configuration
@Slf4j
public class InterceptorConfig implements WebMvcConfigurer {
    @Bean
    public LoginInterceptor loginInterceptor() {
        return new LoginInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor())
                .addPathPatterns("/user/*")
                //排查不拦截的路径
                .excludePathPatterns("/user/login","/user/product_list");
    }
}

配置拦截器使用户登录的接口和商品列表查询的接口不进行token验证,将用户的信息放在ThreadLocal中保证每个线程独立内存空间。

11.启动验证

9f25e8214b934ef3800a8af493ea9b4f.jpg


afcbd20c1b844fea9cff94c499a80765.jpg

249ca6e9360f4969b8e8c7d9e964c7f5.jpg


99ac91122adb42528f615e9483ac17e6.jpg

至此,整个登录整合JWT功能已经开发完成,这块其实还可以根据自己的业务去返回一个RefreshToken,Token过期刷新的token。




相关文章
|
1天前
|
数据管理 API 调度
鸿蒙HarmonyOS应用开发 | 探索 HarmonyOS Next-从开发到实战掌握 HarmonyOS Next 的分布式能力
HarmonyOS Next 是华为新一代操作系统,专注于分布式技术的深度应用与生态融合。本文通过技术特点、应用场景及实战案例,全面解析其核心技术架构与开发流程。重点介绍分布式软总线2.0、数据管理、任务调度等升级特性,并提供基于 ArkTS 的原生开发支持。通过开发跨设备协同音乐播放应用,展示分布式能力的实际应用,涵盖项目配置、主界面设计、分布式服务实现及部署调试步骤。此外,深入分析分布式数据同步原理、任务调度优化及常见问题解决方案,帮助开发者掌握 HarmonyOS Next 的核心技术和实战技巧。
110 76
鸿蒙HarmonyOS应用开发 | 探索 HarmonyOS Next-从开发到实战掌握 HarmonyOS Next 的分布式能力
|
3月前
|
存储 SQL 微服务
常用的分布式事务解决方案(三)
常用的分布式事务解决方案(三)
|
2天前
|
物联网 调度 vr&ar
鸿蒙HarmonyOS应用开发 |鸿蒙技术分享HarmonyOS Next 深度解析:分布式能力与跨设备协作实战
鸿蒙技术分享:HarmonyOS Next 深度解析 随着万物互联时代的到来,华为发布的 HarmonyOS Next 在技术架构和生态体验上实现了重大升级。本文从技术架构、生态优势和开发实践三方面深入探讨其特点,并通过跨设备笔记应用实战案例,展示其强大的分布式能力和多设备协作功能。核心亮点包括新一代微内核架构、统一开发语言 ArkTS 和多模态交互支持。开发者可借助 DevEco Studio 4.0 快速上手,体验高效、灵活的开发过程。 239个字符
134 13
鸿蒙HarmonyOS应用开发 |鸿蒙技术分享HarmonyOS Next 深度解析:分布式能力与跨设备协作实战
|
9天前
|
NoSQL Java Redis
秒杀抢购场景下实战JVM级别锁与分布式锁
在电商系统中,秒杀抢购活动是一种常见的营销手段。它通过设定极低的价格和有限的商品数量,吸引大量用户在特定时间点抢购,从而迅速增加销量、提升品牌曝光度和用户活跃度。然而,这种活动也对系统的性能和稳定性提出了极高的要求。特别是在秒杀开始的瞬间,系统需要处理海量的并发请求,同时确保数据的准确性和一致性。 为了解决这些问题,系统开发者们引入了锁机制。锁机制是一种用于控制对共享资源的并发访问的技术,它能够确保在同一时间只有一个进程或线程能够操作某个资源,从而避免数据不一致或冲突。在秒杀抢购场景下,锁机制显得尤为重要,它能够保证商品库存的扣减操作是原子性的,避免出现超卖或数据不一致的情况。
42 10
|
27天前
|
JSON 安全 算法
Spring Boot 应用如何实现 JWT 认证?
Spring Boot 应用如何实现 JWT 认证?
63 8
|
1月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
40 5
|
1月前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
2月前
|
程序员
后端|一个分布式锁「失效」的案例分析
小猿最近很苦恼:明明加了分布式锁,为什么并发还是会出问题呢?
36 2
|
2月前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
|
2月前
|
NoSQL Java Redis
开发实战:使用Redisson实现分布式延时消息,订单30分钟关闭的另外一种实现!
本文详细介绍了 Redisson 延迟队列(DelayedQueue)的实现原理,包括基本使用、内部数据结构、基本流程、发送和获取延时消息以及初始化延时队列等内容。文章通过代码示例和流程图,逐步解析了延迟消息的发送、接收及处理机制,帮助读者深入了解 Redisson 延迟队列的工作原理。