SpringBoot 整合JWT实现基于自定义注解的-登录请求验证拦截(保姆级教学,附:源码)

news2024/11/24 20:30:58

学习目标:

Spring Boot 整合JWT实现基于自定义注解的 登录请求接口拦截

例:

  • 一篇掌握 JWT 入门知识

 1.1 在学习SpringBoot 整合JWT之前,我们先来说说JWT进行用户身份验证的流程

  • 1:客户端使用用户名和密码请求登录
    2:服务端收到请求,验证用户名和密码
    3:验证成功后,服务端会签发一个token,再把这个token返回给客户端
    4:客户端收到token后可以把它存储起来,比如放到cookie中
    5:客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
    6:服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据
1.2 而JWT就是上述流程当中token的一种具体实现方式,其全称是JSON Web Token,官网地址:https://jwt.io/
并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。JWT的认证流程如下:
  • 1:首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探
    2:后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
    3:后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可
    4:前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)
    后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等
    5:验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

 1.3  JWT结构

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串

JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

 

 1.3.1 Header

JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存
{
  "alg": "HS256",
  "typ": "JWT"
}

 1.3.2 Payload

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

 除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下例:

{
  "sub": "1234567890",
  "name": "Helen",
  "admin": true
}

 请注意:默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中,以防止信息泄露。JWT只是适合在网络中传输一些非敏感的信息

JWT 解码工具:在线JWT Token解析解码工具_x@lijun的博客-CSDN博客

 1.3.3 Signature

签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象

  • 注意JWT每部分的作用,在服务端接收到客户端发送过来的JWT token之后:
    
        header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据
        signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,secretKey实际上代表的是盐值


SpringBoot 整合JWT实现基于自定义注解的-实现一个登录请求验证拦截

1:创建数据库结构

CREATE TABLE `users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

2: 创建SpringBoot(2.3.5.RELEASE版本)工程,引入pom依赖

pom.xml

 <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- MP -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- jwt -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
        <!--  hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

3: application.properties 文件

server.port=8900
spring.application.name=jwt

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.log4j2.Log4j2Impl
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/peixun?serverTimezone=GMT%2B8
spring.datasource.username=root

 4:JWTUtil 工具类

public class JWTUtils {
    private static final String APP_SECRET  = "ukc8BDbRigUDaY6pZFfWus2jZWLPHOsdadasdasfdssfeweee";
    /**
     * 生成token
     * @param map  传入payload
     * @return 返回token
     */
    public static String getToken(Map<String,String> map){
        Calendar instance = Calendar.getInstance();
//        instance.add(Calendar.MILLISECOND,20);
        instance.add(Calendar.DAY_OF_WEEK,7);
        // 创建 JWT builder
        JWTCreator.Builder builder = JWT.create();
        // payload
        map.forEach(builder::withClaim);
        // 设置过期时间
        builder.withExpiresAt(instance.getTime());
        // 设置签名加密
        String token = builder.sign(Algorithm.HMAC256(APP_SECRET));
        return token;
    }
    /**
     * 验证token
     * @param token
     * @return
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(APP_SECRET)).build().verify(token);
    }
    /**
     * 获取token中payload
     * @param token
     * @return
     */
    public static DecodedJWT getToken(String token){
        return JWT.require(Algorithm.HMAC256(APP_SECRET)).build().verify(token);
    }
}

 5:封装统计返回结果类

Result

public class Result implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    //状态码
    private Integer code;

    //响应消息
    private String msg;

    //响应数据
    private Object data;
    private Integer count;

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Result(ResultCode resultCode,Object data) {
        this.code=resultCode.getCode();
        this.msg=resultCode.getMsg();
        this.data=data;
    }
    public Result(Integer code,String msg,Object data) {
        this.code=code;
        this.msg=msg;
        this.data=data;
    }

}

 ResultCode

public enum ResultCode {

    //成功
    SUCCESS(200,"成功"),
    PARAM_IS_INVALID(1001,"参数无效"),
    PARAM_IS_BLANK(1002,"参数为空"),
    PARAM_IS_ERROR(1004,"参数错误"),
    PARAM_IS_COMPLETE(1003,"参数缺失"),
    USER_LONGIN_ERROR(2001,"账号或密码错误"),
    USER_LONGIN_EXIST(2002,"用户不存在"),
    USER_LONGIN_EXISTD(2003,"用户已存在"),
    KHXX_EXISTD(2010,"客户信息已存在!"),
    USER_LONGIN_EXAMINE(2004,"用户待审核"),
    UPDATE_EXAMINE(2006,"修改申请已提交,等待管理员审核"),
    USER_LONGIN_STOP(2005,"用户已停用"),
    ERROR(500,"操作失败"),
    CUSTOMER_EXISTD(400,"客户信息已存在"),
    PHONE_ERROR(402,"验证码或者手机号错误"),
    PHONE_EXIST(405,"手机号为空"),
    PHONE_RETRY(405,"请2分钟后重试"),
    PHONE_FPRMATEXIST(406,"手机号错误"),
    SINE_ERROR(401,"用户信息已过期,请重新登录"),
    LONG_OVERTIME(406,"登录超时,请重新登录"),
    KHXX_HTJH_EXISTD(1005,"该类型的计划已存在"),
    USER_INSUFFICIENT_AUTHORITY(403,"权限不足"),
    FILE_NOTBLANK(2007,"上传文件不能为空"),
    FILE_MAXSIZE(2008,"上传文件不能超过2M"),
    FILE_ERRORSUFFIX(2009,"上传文件错误,只能上传文档或表格以及PDF格式");

    private Integer code;

    private String msg;

    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    ResultCode(Integer code,String msg){
        this.code=code;
        this.msg=msg;
    }

}

上述准备工作以及完成

6:pojo 实体类

@Data
public class Users {
    private String id;
    private String username;
    private String password;
}

7: annotation 自定义注解

/**
 * @author xia
 * @version 1.0
 * @Data 用来跳过验证的 PassToken
 * @date 2023/2/13 16:16
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}
/**
 * @author xia
 * @version 1.0
 * @DATA  用于登录后才能操作的token
 * @date 2023/2/13 16:16
 */

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
    boolean required() default true;
}

8:拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor())
                .excludePathPatterns("/user/**")
                .addPathPatterns("/**");
    }
    @Bean
    public JWTInterceptor jwtInterceptor() {
        return new JWTInterceptor();
    }
}
@Component
@SuppressWarnings("all")
public class JWTInterceptor implements HandlerInterceptor {
    @Autowired
    User user;
    // 请求前到达之前拦截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object)
            throws Exception {
        // 从请求头获取 token
        String token = request.getHeader("token");
        //首先映射是不是方法,如果不是则返回
        if(!(object instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod=(HandlerMethod) object;
        Method method=handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }

        // token 验证失败后的返回信息
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                if (StrUtil.isBlank(token)) {
                    throw new RuntimeException( "无token,请重新登录");
                }
                // 获取 token 中的 user id
                int userId;
                try {
                    DecodedJWT decode = JWT.decode(token);
                    String id = decode.getClaim("id").asString();
                    userId = Integer.parseInt(id);

                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                System.out.println(userId);
                Users users = user.findUserById(userId);
                if (users == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                try {
                    // JWT 工具包验证 token
                    JWTUtils.verify(token);
                    return true;
                } catch (TokenExpiredException e) {
                    throw new RuntimeException("Token已经过期!!!");
                } catch (SignatureVerificationException e){
                    throw new RuntimeException("签名错误!!!");
                } catch (AlgorithmMismatchException e){
                    throw new RuntimeException("加密算法不匹配!!!");
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("无效token~~");
                }
            }
        }
        return true;
    }
}

 9:controller 层

@RestController
@Slf4j
@SuppressWarnings("all")
public class JwtController {
    @Resource
    private UsersMapper mapper;

    // 认证

    @PassToken
    @PostMapping("/user/login")
    public Result login(@RequestBody Users user) {
        System.out.println("前端传来的用户数据:"+user);
        try {
            // 查询数据库
            QueryWrapper<Users> queryWrapper = new QueryWrapper<>();
            HashMap<String, Object> queryMap = new HashMap<>();
            queryMap.put("username", user.getUsername());
            queryMap.put("password", user.getPassword());
            queryWrapper.allEq(queryMap);
            Users userDb = mapper.selectOne(queryWrapper);
            // 认证失败
            if(userDb == null) {
                throw new RuntimeException("没有此用户。请重新登录");
            }
            // 认证成功
            Map<String, String> tokenClaimsMap = new HashMap<>(); //用来存放payload
            tokenClaimsMap.put("id",userDb.getId());
            tokenClaimsMap.put("username", userDb.getUsername());
            String token = JWTUtils.getToken(tokenClaimsMap);
            // 返回的数据
           return new Result(ResultCode.SUCCESS,token);
        } catch (Exception e) {
            // 认证失败,返回的数据
            e.printStackTrace();
            return new Result(ResultCode.ERROR,e);
        }
    }

    @UserLoginToken
    @GetMapping("/private/info")
    public String info(HttpServletRequest request){
        String token = request.getHeader("token");
        DecodedJWT tokenJWT = JWTUtils.getToken(token);
        System.out.println(tokenJWT.getClaim("username").asString());
        System.out.println(tokenJWT.getClaim("userId").asInt());
        return "这是一段私人信息。只有登录才能显示";
    }
    @UserLoginToken
    @GetMapping("/private/Test")
    public Result info(){
        String code = "这是一段私人信息。只有登录才能显示";
        return new Result(ResultCode.SUCCESS,code);
    }
    @PassToken()
    @PostMapping("/private/Test2")
    public Result info2(){
        String code = "这是一段普通信息。不登录也能显示";
        return new Result(ResultCode.SUCCESS,code);
    }
}

10: service 以及实现类

@Service
public interface User {
    Users findUserById(int userId);
}
@Service
public class UserService implements User{
    @Resource
    private UsersMapper mapper;
    @Override
    public Users findUserById(int userId) {
        Users users = mapper.selectById(userId);
        System.out.println("------------>"+ users);
        return users;
    }
}

11: mapper层


@Mapper
public interface UsersMapper extends BaseMapper<Users> {
}

启动项目,演示效果如下:

1:账号登录成功生成Token (登录请求,使用@PassToken() 注解 跳过token验证)

    @PassToken
    @PostMapping("/user/login")
    public Result login(@RequestBody Users user) {
        System.out.println("前端传来的用户数据:"+user);
        try {
            // 查询数据库
            QueryWrapper<Users> queryWrapper = new QueryWrapper<>();
            HashMap<String, Object> queryMap = new HashMap<>();
            queryMap.put("username", user.getUsername());
            queryMap.put("password", user.getPassword());
            queryWrapper.allEq(queryMap);
            Users userDb = mapper.selectOne(queryWrapper);
            // 认证失败
            if(userDb == null) {
                throw new RuntimeException("没有此用户。请重新登录");
            }
            // 认证成功
            Map<String, String> tokenClaimsMap = new HashMap<>(); //用来存放payload
            tokenClaimsMap.put("id",userDb.getId());
            tokenClaimsMap.put("username", userDb.getUsername());
            String token = JWTUtils.getToken(tokenClaimsMap);
            // 返回的数据
           return new Result(ResultCode.SUCCESS,token);
        } catch (Exception e) {
            // 认证失败,返回的数据
            e.printStackTrace();
            return new Result(ResultCode.ERROR,e);
        }

2:使用这个注解 :@UserLoginToken(登录需要验证)

    @UserLoginToken
    @GetMapping("/private/info")
    public String info(HttpServletRequest request){
        String token = request.getHeader("token");
        DecodedJWT tokenJWT = JWTUtils.getToken(token);
        System.out.println(tokenJWT.getClaim("username").asString());
        System.out.println(tokenJWT.getClaim("userId").asInt());
        return "这是一段私人信息。只有登录才能显示";
    }

    @UserLoginToken
    @GetMapping("/private/Test")
    public Result info(){
        String code = "这是一段私人信息。只有登录才能显示";
        return new Result(ResultCode.SUCCESS,code);
    }

 不携带Token 打印结果:

 

 Token 错误:

3: 使用@PassToken() 放行请求 ,即不需要Token 也可登录

    @PassToken()
    @PostMapping("/private/Test2")
    public Result info2(){
        String code = "这是一段普通信息。不登录也能显示";
        return new Result(ResultCode.SUCCESS,code);
    }

源码地址如下:

SpringBoot 整合 jwt: 学习token的1111

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/346446.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring中@Autowire和@Resource的区别在哪里?

介绍今天使用Idea写代码的时候&#xff0c;看到之前的项目中显示有warning的提示&#xff0c;去看了下&#xff0c;是如下代码?Autowire private JdbcTemplate jdbcTemplate;提示的警告信息Field injection is not recommended Inspection info: Spring Team recommends: &quo…

AntDB-M设计之内存结构

亚信科技专注通信行业多年&#xff0c;AntDB数据库从诞生开始&#xff0c;就面对通信级的大数据量应用场景挑战&#xff0c;在性能、稳定性、规模化等方面获得了超过10年的通信核心业务系统验证&#xff0c;性能峰值达到每秒百万的通信核心交易量。AntDB-M&#xff08;AntDB内存…

CMMI有哪几个级别,每个级别有哪些其特征

CMMI 一共分五个级别&#xff0c;一级低&#xff0c;五级高&#xff0c;一般初次认证CMMI从三级或者二级开始。 1、CMMI一级&#xff0c;完成级。 在完成级水平上&#xff0c;企业对项目的目标与要做的努力很清晰。项目的目标得以实现。一般来说&#xff0c;公司的初始阶段就是…

工作记录------@Accessors(chain = true)引起的BUG,Excel导入时获取不到值

工作记录------Accessors(chain true)引起的BUG&#xff0c;Excel导入时获取不到值 如题所示 背景&#xff1a;在进行文件excel文件导入时&#xff0c;发现实体类获取到的属性值都为null。 框架&#xff1a;com.alibaba.excel 2.2.0的版本。 结论&#xff1a;首先说下结论 如…

2021年职业院校技能大赛“网络安全”项目江西省A模块

目录 一、竞赛时间 三、竞赛任务书内容 &#xff08;一&#xff09;拓扑图 &#xff08;二&#xff09;A模块基础设施设置/安全加固&#xff08;200分&#xff09; A-1&#xff1a;登录安全加固 1.密码策略&#xff08;Web&#xff09; a.最小密码长度不少于8个字符&…

C++入门基础

本章内容&#xff1a; 一、C前言 1. 什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危机&#xff0c; 20世纪80年代&#x…

笔记本连接wifi,浏览器访问页面,显示访问被拒绝

打开chrome、edge浏览器访问第1个第2个页面正常&#xff0c;后面再打开页面显示异常。 但手机连接正常&#xff0c;笔记本连接异常&#xff0c;起初完全没有怀疑是wifi问题 以为用了vpn软件问题&#xff0c;认为中了病毒。杀毒&#xff0c;并没有中毒。 1、关闭vpn代理&#…

基于Java+Spring+vue+element商城销售平台设计和实现

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

【Call for papers】ICCV-2023(CCF-A/人工智能/2023年3月8日截稿)

ICCV is the premier international computer vision event comprising the main conference and several co-located workshops and tutorials. 文章目录1.会议信息2.时间节点1.会议信息 会议介绍&#xff1a; ICCV是主要的国际计算机视觉活动&#xff0c;包括主要会议和几个…

【LeetCode】剑指 Offer 03. 数组中重复的数字 -- Java Version

题目链接&#xff1a; https://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/ 1. 题目介绍&#xff08;03. 数组中重复的数字&#xff09; 找出数组中重复的数字。 在一个长度为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重…

linux篇【15】:应用层-网络https协议

目录 一.HTTPS介绍 1.HTTPS 定义 2.HTTP与HTTPS &#xff08;1&#xff09;端口不同&#xff0c;是两套服务 &#xff08;2&#xff09;HTTP效率更高&#xff0c;HTTPS更安全 3.加密&#xff0c;解密&#xff0c;密钥 概念 4.为什么要加密&#xff1f; 5.常见的加密方式…

TransE及其实现

TransE 该模型将关系和实体表示为同一空间中的向量&#xff0c;给定事实&#xff08;h,r,s&#xff09;&#xff08;h,r,s&#xff09;&#xff08;h,r,s&#xff09;关系 rrr 的向量 rrr被解释为头实体向量 hhh与尾实体向量 ttt之间的平移,因此嵌入实体hhh和ttt可以通过平移向…

分享82个HTML电子产品模板,总有一款适合您

分享82个HTML电子产品模板&#xff0c;总有一款适合您 82个HTML电子产品模板下载链接&#xff1a;https://pan.baidu.com/s/106NtZkrVefSFGGS54xk-kA?pwdbvn8 提取码&#xff1a;bvn8 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 import os import shutil …

TongWeb8防止System.exit代码导致的进程停止

现象&#xff1a;当应用中存在System.exit 、Runtime.exit代码执行时&#xff0c;会导致TongWeb进程停止&#xff0c;从而产生如下日志&#xff1a;2023-02-14 09:47:36 [WARN] - The web application [webtest01] is still processing a request that has yet to finish. This…

LeetCode 19.删除链表的倒数第 N 个结点

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 给你一个链表&#xff0c;删除链表的倒数第 nnn 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#x…

快速部署个人导航页:美好的一天从井然有序开始

很多人都习惯使用浏览器自带的收藏夹来管理自己的书签&#xff0c;然而收藏夹存在着一些问题。 经过长时间的累积&#xff0c;一些高频使用的重要网站和偶尔信手收藏的链接混在了一起&#xff0c;收藏夹因为内容过多而显得杂乱无章&#xff1b;收藏夹没有什么美观可言&#xf…

【java】Spring Boot --40 个 Spring Boot 常用注解(建议收藏)

本文目录一、Spring Web MVC 注解Spring Web MVC 注解RequestMappingRequestBodyGetMappingPostMappingPutMappingDeleteMappingPatchMappingControllerAdviceResponseBodyExceptionHandlerResponseStatusPathVariableRequestParamControllerRestControllerModelAttributeCross…

sqlServer 2019 开发版(Developer)下载及安装

下载软件 官网只有2022的&#xff0c;2019使用百度网盘进行下载 安装下崽器 选择自定义安装 选择语言、以及安装位置 点击“安装” 安装 SQL Server 可能的故障 以上步骤安装后会弹出以上界面&#xff0c;如果未弹出&#xff0c;手动去安装目录下点击 SETUP.EXE 文件…

数据分析与SAS学习笔记1

数据分析的六层模型&#xff1a; 1&#xff09;数据源层&#xff1a;数据分析的数据源&#xff1b;DBA&#xff1b;初加工&#xff1b;对数据源按某些规则进行抽取&#xff0c;ETL&#xff1b; 2&#xff09;数据仓库层&#xff1a;OLAP的功能&#xff0c;联机事务处理。OLTP、…

这才叫装机必备 , 这4款电脑软件超级实用,用一次就爱上

好用又免费的软件犹如被掩盖的珍珠&#xff0c;一旦发现了&#xff0c;让你满眼欣喜。 1、HiBit Uninstaller 这是一款大小不到10M的超实用卸载软件&#xff0c;完全免费无任何广告&#xff0c;兼顾垃圾清理、流氓软件卸载等超多实用功能&#xff0c;流氓软件卸载能力比某安全卫…