JWT、session、token区别和实现

news2024/11/6 3:01:50

JWT、session、token区别和实现

这里需要用到Redis和JWT。

springboot版本是3.2.1

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
    <version>4.3.0</version>
</dependency>
<!--redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.14.1</version>
</dependency>

<!--通用基础配置boottest/lombok/hutool-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.6</version>
</dependency>

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.7.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <scope>test</scope>
</dependency>
<!-- JWT需要的包 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.32</version>
</dependency>
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.3.0</version>
</dependency>

Knief4j配置

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Knife4jConfig {
    // 创建 admin API分组
    @Bean
    public GroupedOpenApi sessionApi() {
        return GroupedOpenApi.builder()
                .group("session接口")
                .pathsToMatch("/session/**")
                .build();
    }

    // 创建 token API分组
    @Bean
    public GroupedOpenApi tokenApi() {
        return GroupedOpenApi.builder()
                .group("token接口")
                .pathsToMatch("/token/**")
                .build();
    }

    // 创建 jwt API分组
    @Bean
    public GroupedOpenApi JWTApi() {
        return GroupedOpenApi.builder()
                .group("jwt接口")
                .pathsToMatch("/jwt/**")
                .build();
    }

    @Bean
    public OpenAPI customerOpenApi() {
        return new OpenAPI().info(
                new Info()
                .title("接口文档")
                .version("1.0")
                .contact(new Contact()
                .name("bunny")));
    }
}

applicatuion.xml配置

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

  data:
    redis:
      database: 0
      host: 192.168.31.140
      port: 6379

在这里插入图片描述

session

简介

session是浏览器存在内存中的一种验证方式,基于会话式的,如果服务器重启或者是浏览器关闭这一次会话结束才会重新生成新的session。

或者是设置session的过期时间,当到达过期时间时session也会失效

但是session好处在于,如果设定时间时半小时,如果这段时间用户一直在操作那么session会自动延长过期时间,当在这段时间内用户一直未操作那么才会过期。

优点
  1. 状态保持:会话允许服务器在多个页面请求之间保持用户的状态。这意味着用户在网站上浏览时,服务器可以跟踪他们的活动,包括登录状态、购物车内容等。
  2. 安全性:通过会话,服务器可以在不暴露用户信息的情况下跟踪用户的活动。相比于将用户信息直接存储在客户端(如cookie)中,使用会话可以提供更高的安全性。
  3. 用户体验:会话可以改善用户体验,因为它可以使用户在网站上进行交互时更加流畅,而不需要在每个页面都重新进行身份验证或者重新输入信息。
  4. 个性化服务:通过会话,网站可以根据用户的活动和偏好提供个性化的服务,比如推荐商品、定制内容等。
缺点
  1. 服务器负载:使用会话需要服务器在一段时间内保持用户状态,这可能会增加服务器的负载,尤其是在大量用户同时访问时。
  2. 存储开销:会话数据通常需要存储在服务器端,这可能会增加存储开销,尤其是对于大型网站来说。
  3. 跨设备问题:会话通常是与特定设备或浏览器相关联的,这可能导致在不同设备或浏览器上的一致性问题。
  4. 隐私问题:如果会话数据不正确处理或者未加密,可能会导致隐私问题,比如会话劫持或会话固定攻击。

session是比较安全的,缺点是不能跨设备,比如分布式中。我在和服务器A进行通信,这时候接口需要和服务器B进行通信时是不可能实现的因为是基于会话的,所以需要重新登录。

虽然说不可跨设备,在后面中我也会介绍session如何跨设备,这里需要使用到Redis,将信息存储在Redis中这样就可以做到跨设备了。

需重新登录
用户
服务器A
服务器B

关于session存储在cookies中不安全的问题?

这里的案例会在后面代码中写到。

打开控制台找到应用,存储在这里面的内容是基于domain存储的,其实就是按照域名访问的,不同域名之间是相互隔绝的,如果说存储在cookie中不安全其实存储在这里任何东西都不安全,照我来说的话。

只要代码写的没问题,不跨域之间都是相互隔绝的。只不过session会存在无法跨设备(可以自己手动解决下面会说到)。

在这里插入图片描述

具体实现

SessionController

其中需要用到HttpSessionpublic String getSessionInfo(HttpSession session),这样就可以操作session了。

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/session")
@Tag(name = "session请求相关", description = "session请求相关")
public class SessionController {
    private static final String SESSION_KEY = "session_key";

    /**
     * 判断消息是否为空
     *
     * @param message 消息
     * @return 是否为空,布尔值
     */
    private boolean isEmpty(String message) {
        return message == null || message.trim().isEmpty();
    }

    /**
     * @param session session
     * @param message 罅隙
     * @return 保存session信息成功消息
     */
    @RequestMapping("saveSessionInfo")
    @Operation(summary = "请求session")
    public String saveSessionInfo(HttpSession session, String message) {
        if (isEmpty(message)) {
            return "messages不能为空";
        }
        session.setAttribute(SESSION_KEY, message);
        return "保存session信息成功,sessionId:" + session.getId();
    }

    @RequestMapping("getSessionInfo")
    @Operation(summary = "获取session", description = "获取session")
    public String getSessionInfo(HttpSession session) {
        return "获取session消息为:" + session.getAttribute(SESSION_KEY);
    }
}
实际操作

打开浏览器,可以使用接口文档测试或者是浏览器进行测试都可以。

假设message中存储就是用户信息

在这里插入图片描述

当我们获取session时,浏览器会判断这次会话是否是上次的,如果是的就会获取用户信息,比如在用户登录时,登录完成之后前端不需要再传递userid也能获得用户的登录信息。

在这里插入图片描述

如何实现关闭或者重启服务器也可以获得session

如果说现在需要重启服务器或者关闭浏览器也能获得上次session,方法有多终,下面说的token就是一种将用户信息存储在Redis中模拟session的情况。

或者是重写HttpSession接口,因为HttpSession是使用HttpSession接口实现的,那么我们只需要重写其中的方法自定义去控制也可以完成session自己控制。

在这里插入图片描述

只要重写这三个就可以完成对session的设置、获取、移出。

在这里插入图片描述

  • 这种session有缺点,它是基于会话的,如果浏览器关闭那么session会重新登录
  • 或者服务器重启时也会重新登录因为会重新获取session
  • 又或者没有重启,没有关闭浏览器,如果是分布式的服务器。
  • 当访问服务器(server1)时,这个session是存在 server1中的
  • 如果这时请求接口是在另一个服务器上(server2)那么session会不存在。
  • 因为不是基于同一个会话,但是也有优点。
  • 假如设置session过期时间比如半小时,如果在29:59:59时这时又重新发起请求那么这个session会自动会续时长
  • 如果要想自己操作session,想让session自定义
  • 解决方案:
  • 可以重写session,因为session集成于接口 HttpSession;可以重写它可以实现自己操作session自定义
  • 或者将session放在redis中

token

目录结构见上面

Redis序列化配置RedisConfig

这个就不解释了,序列化Redis,学过Redis的都应该知道的。每个人配置方式不同,可以按照自己的方式来,这只是提供参考

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);

        // 设置key序列化为String
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 设置value序列化方式为JSON,使用GenericJackson2JsonRedisSerializer替换为默认序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        return redisTemplate;
    }
}

实际操作

先将代码粘上来,后面逐一说明其作用。

import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.http.HttpRequest;
import java.util.UUID;

@RestController
@RequestMapping("/token")
@Tag(name = "基于Redis存储session", description = "Redis存储token")
public class RedisSessionController {
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 判断消息是否为空
     *
     * @param message 消息
     * @return 是否为空,布尔值
     */
    private boolean isEmpty(String message) {
        return message == null || message.trim().isEmpty();
    }


    @RequestMapping("saveSessionByToken")
    @Operation(summary = "设置session使用token", description = "设置session使用token")
    public String saveSessionByToken(String message) {
        if (isEmpty(message)) {
            return "messages不为空";
        }

        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, message);
        return "保存session信息成功,sessionId=" + token;
    }

    @RequestMapping("getSessionByToken")
    @Operation(summary = "获取token使用token", description = "获取token使用token")
    public String getSessionByToken(String token) {
        if (isEmpty(token)) {
            return "token不为空";
        }
        return "获取的token的信息为:" + redisTemplate.opsForValue().get(token);
    }

    @RequestMapping("getSessionByTokenWithCookie")
    @Operation(summary = "设置token使用cookies", description = "设置token使用cookies")
    public String getSessionByTokenWithCookie(HttpServletResponse response, String message) {
        if (isEmpty(message)) {
            return "message不能为空";
        }

        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, message);
        Cookie cookie = new Cookie("token", token);
        cookie.setMaxAge(-1);// 设置有效期
        response.addCookie(cookie);
        return "保存token信息成功,token:" + token;
    }

    @RequestMapping("getByTokenWithCookie")
    @Operation(summary = "获取token使用cookies", description = "获取token使用cookies")
    public String getByTokenWithCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();// 因为cookie是一个数组,浏览器中有很多cookie
        String token = null;

        if (cookies == null) return "信息为空";

        // 当找到cookie时就跳出
        for (Cookie cookie : cookies) {
            if ("token".equals(cookie.getName())) {
                token = cookie.getValue();
                break;
            }
        }

        if (isEmpty(token)) return "token不能为空";
        Object message = redisTemplate.opsForValue().get(token);
        return "获取token使用cookies,token:" + token+";获取message使用cookies,message:" + message;// 这里不做转换JSON也可以
        // return "获取token使用cookies,token:" + token+";获取message使用cookies,message:" + JSON.toJSONString(message);// 这里不做转换JSON也可以
    }
}

设置token

将用户信息存储到Redis中,先设置唯一值将这个值放在Redis中,获取时因为这个值是唯一的所以可以在Redis中获取到。判断是否为空的函数isEmpty在上面全部代码中有的。

当然这里也需要讲你Redis服务器打开,否则也是没用的。

@RequestMapping("saveSessionByToken")
@Operation(summary = "设置session使用token", description = "设置session使用token")
public String saveSessionByToken(String message) {
    if (isEmpty(message)) {
        return "messages不为空";
    }

    String token = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set(token, message);
    return "保存session信息成功,sessionId=" + token;
}

在这里插入图片描述

可以发现,存储的值在Redis中了,那么下次就是用这个key去获取用户相关信息

获取token

请求接口时也可以正常的拿到所需要的值

@RequestMapping("getSessionByToken")
@Operation(summary = "获取token使用token", description = "获取token使用token")
public String getSessionByToken(String token) {
    if (isEmpty(token)) {
        return "token不为空";
    }
    return "获取的token的信息为:" + redisTemplate.opsForValue().get(token);
}

在这里插入图片描述

将token存储在cookie中

在cookie中可以设置setMaxAge来设置有效期

@RequestMapping("getSessionByTokenWithCookie")
@Operation(summary = "设置token使用cookies", description = "设置token使用cookies")
public String getSessionByTokenWithCookie(HttpServletResponse response, String message) {
    if (isEmpty(message)) {
        return "message不能为空";
    }

    String token = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set(token, message);
    Cookie cookie = new Cookie("token", token);
    cookie.setMaxAge(-1);// 设置有效期
    response.addCookie(cookie);
    return "保存token信息成功,token:" + token;
}

在这里插入图片描述

从cookie中获取token

这时候就不需要前端再传递当前登录时这个用户的id,直接请求这个接口就可以获取到用户登录的信息等。

因为存储在cookie中的,所以下次请求带上这个cookie就可以了。

@RequestMapping("getByTokenWithCookie")
@Operation(summary = "获取token使用cookies", description = "获取token使用cookies")
public String getByTokenWithCookie(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();// 因为cookie是一个数组,浏览器中有很多cookie
    String token = null;

    if (cookies == null) return "信息为空";

    // 当找到cookie时就跳出
    for (Cookie cookie : cookies) {
        if ("token".equals(cookie.getName())) {
            token = cookie.getValue();
            break;
        }
    }

    if (isEmpty(token)) return "token不能为空";
    Object message = redisTemplate.opsForValue().get(token);
    return "获取token使用cookies,token:" + token+";获取message使用cookies,message:" + message;// 这里不做转换JSON也可以
    // return "获取token使用cookies,token:" + token+";获取message使用cookies,message:" + JSON.toJSONString(message);// 这里不做转换JSON也可以
}

在这里插入图片描述

JWT

目录结构见最上面开头截图。

JWT这个就有点鸡肋了,感觉现在没有多少用JWT了,因为不安全,比如说使用session或者是Redis存储,举个例子;我想让这个用户下线,只需要在后面将这个用户剔除就行了。

但是JWT不行,只要她的信息没有过期而且可以被解析,她就认为这个服务器就是她主人。

而且她的信息是可以被解析的,因为信息是base64编码的,并不是加密的,你可以去网上找一个可以解析base64的,看看是否可以被解析。

当然我这里就不去网上找解析base64网站了,因为浏览器控制台为我们提供了这个函数叫atob,也是JavaScript中自带的,如果想把字符串弄成base64可以使用函数btoa()

JWT工具类JWTUtils

先把这个工具类放上,目录结构看上面的就可以了。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONWriterUTF16JDK8UF;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import io.swagger.v3.core.util.Json;
import nonapi.io.github.classgraph.json.JSONUtils;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component("jwtUtils")
public class JWTUtils<T> {
    private static final String SECRET = "test123456";

    public String createToken(String key, T data, Integer expireSeconds) {
        String token = null;
        try {
            // 因为
            Date date = new Date(System.currentTimeMillis() + expireSeconds + 100000);
            token = JWT.create()
                    .withClaim(key, JSON.toJSONString(data))
                    .withExpiresAt(date)
                    .sign(Algorithm.HMAC256(SECRET));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }

    public <T> T getTokenData(String key, String token, Class<T> tClass) {
        try {
            if (null == token || token.isEmpty()) {
                return null;
            }

            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            DecodedJWT jwt = verifier.verify(token);
            String jsonData = jwt.getClaim(key).asString();
            return JSON.parseObject(jsonData, tClass);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

实际操作

先把全部的代码都放上来。

import com.example.session.ulits.JWTUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@Tag(name = "jwt请求", description = "测试jwt请求")
@RequestMapping("/jwt")
public class JWTController {
    private static final String SESSION_KEY = "session_key";
    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private JWTUtils jwtUtils;

    /**
     * 判断消息是否为空
     *
     * @param message 消息
     * @return 是否为空,布尔值
     */
    private boolean isEmpty(String message) {
        return message == null || message.trim().isEmpty();
    }


    @RequestMapping("saveMyJWT")
    @Operation(summary = "保存JWT信息")
    public String saveMyJWT(HttpServletResponse response, String message) {
        if (isEmpty(message)) {
            return "message不能为空";
        }
        // 设置JWT的key值和消息,还有过期时间这里是10秒
        String token = jwtUtils.createToken(SESSION_KEY, message, 100);
        Cookie cookie = new Cookie("token", token);
        response.addCookie(cookie);// 将这个JWT的信息保存在cookie中
        return "保存信息token成功,token:" + token;
    }

    @RequestMapping("getByJwt")
    @Operation(summary = "获取JWT")
    public String getByJwt(String token) {
        if (isEmpty(token)) {
            return "token不能为空";
        }

        String message = (String) jwtUtils.getTokenData(SESSION_KEY, token, String.class);
        return "保存token信息成功,token的:" + message;
    }
}
保存JWT信息

这里的消息我就不写用户信息随便写一个message,看看中文如何被解析的

@RequestMapping("saveMyJWT")
@Operation(summary = "保存JWT信息")
public String saveMyJWT(HttpServletResponse response, String message) {
    if (isEmpty(message)) {
        return "message不能为空";
    }
    String token = jwtUtils.createToken(SESSION_KEY, message, 10);
    redisTemplate.opsForValue().set(token, message);
    Cookie cookie = new Cookie("token", token);
    response.addCookie(cookie);
    return "保存信息token成功,token:" + token;
}

附上JavaScript解析中文的代码,如果你的是英文的就不需要这样做了,这里只是做一个扩展。

function b64_to_utf8(str) {
  return decodeURIComponent(escape(atob(str)));
}

let base64String = "5L2g5aW977yM5LiW55WM77yB";
let chineseString = b64_to_utf8(base64String);
console.log(chineseString);

可以看到JWT的信息已经保存在cookie中了。

在这里插入图片描述

将上图中,中间的这串字符串传递到刚刚写的JavaScript代码中。可以看到信息已经被解析了。

它的字符串是用.来分割的,所以每个.后面的字符串都是base64,也就是说都可以被解析的。

function b64_to_utf8(str) {
  return decodeURIComponent(escape(atob(str)));
}

let base64String = "eyJzZXNzaW9uX2tleSI6Ilwi5oiR55qE5ZWK5a6e5omT5a6eXCIiLCJleHAiOjE3MDM0MTc5Njh9";
let chineseString = b64_to_utf8(base64String);
console.log(chineseString);

在这里插入图片描述

获取JWT信息

之后使用Java的代码获取JWT信息,这里就不使用cookie遍历了,简单的实现下。

@RequestMapping("getByJwt")
@Operation(summary = "获取JWT")
public String getByJwt(String token) {
    if (isEmpty(token)) {
        return "token不能为空";
    }

    String message = (String) jwtUtils.getTokenData(SESSION_KEY, token, String.class);
    return "保存token信息成功,token的:" + message;
}

的。

function b64_to_utf8(str) {
  return decodeURIComponent(escape(atob(str)));
}

let base64String = "eyJzZXNzaW9uX2tleSI6Ilwi5oiR55qE5ZWK5a6e5omT5a6eXCIiLCJleHAiOjE3MDM0MTc5Njh9";
let chineseString = b64_to_utf8(base64String);
console.log(chineseString);

[外链图片转存中…(img-K7dL767V-1703419158667)]

获取JWT信息

之后使用Java的代码获取JWT信息,这里就不使用cookie遍历了,简单的实现下。

@RequestMapping("getByJwt")
@Operation(summary = "获取JWT")
public String getByJwt(String token) {
    if (isEmpty(token)) {
        return "token不能为空";
    }

    String message = (String) jwtUtils.getTokenData(SESSION_KEY, token, String.class);
    return "保存token信息成功,token的:" + message;
}

在这里插入图片描述

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

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

相关文章

vitis HLS中实现canny算法的IP核

一、前言 canny边缘检测主要用于提取图像的边缘&#xff0c;是最常用且有效的边缘检测算法。在AMD赛灵思提供的库函数中&#xff0c;使用xf::cv::Canny和xf::cv::EdgeTracing两个函数实现canny边缘提取。本文举例说明如何在vitis HLS 2023.1中实现canny算法。 二、xf::cv::Cann…

GoogLeNet(V1)

目录 一、GooLeNet介绍 1、模型设计的motivation 2、Inception块 3、GoogLeNet架构 4、Inception后续变种 5、总结 二、代码实现 1、Inception块 2、GoogLeNet模型 3、训练模型 4、总结 一、GooLeNet介绍 GoogLeNet是由Google团队于2014年提出的深度卷积神经网络架构…

c++缺省参数与函数重载(超详细)

文章目录 前言一、缺省参数1.缺省参数的概念与使用2.缺省参数的分类3.缺省参数注意事项 二、函数重载1.什莫事函数重载2.函数重载的几种形式3.函数重载与缺省值的结合4.为什么c支持函数重载&#xff1f;&#xff1f; 总结 前言 在本文章中&#xff0c;我们将要详细介绍一下Cc缺…

【MySQL】数据库之索引的增删改查

目录 一、索引是什么 二、索引的作用 三、工作方式 四、创建索引的依据&#xff1a; 五、索引的分类 六、索引的增删改查&#xff08;索引是一种对象&#xff0c;与字段类似是命令&#xff09; 索引的添加 ​编辑第一种&#xff1a;普通索引的创建 第二种&#xff1a;唯…

C++ 比 C语言增加的新特性 2

1.C新增了带默认值参数的函数 1.1 格式 格式&#xff1a;返回值 函数名&#xff08;参数1初始值1&#xff0c;..........&#xff09;{} 例如&#xff1a;void function&#xff08;int a10&#xff09;{} 调用&#xff1a;不需要更改参数的值&#xff1a;function&#x…

001 图书增删改查 SSM MySQL

技术框架&#xff1a;Spring SpringMVC Mybatis JSP MySQL 001 图书增删改查 SSM MySQL package com.demo.controller;import com.demo.pojo.Book; import com.demo.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.spri…

leetcode 面试题 17.19. 消失的两个数字 (hard)(优质解法)

链接&#xff1a;面试题 17.19. 消失的两个数字 代码&#xff1a; class Solution {public int[] missingTwo(int[] nums) {int lengthnums.length;int tmp0;//将完整数据以及 nums 中的数据都进行异或&#xff0c;得到的就是缺失的两个数字 a^b 的结果for(int i1;i<length…

【飞翔的鸟】飞行游戏-uniapp项目开发流程详解

小时候玩过的飞行游戏&#xff0c;叫什么名字来着&#xff0c;通过点击操作控制煽动翅膀来持续飞行&#xff0c;躲避障碍物&#xff0c;有多远就飞多远吧&#xff0c;现在想起来&#xff0c;其中的实现原理非常简单&#xff0c;感兴趣的话来一起看看&#xff0c;这里给大家讲一…

RIS 系列 Mask Grounding for Referring Image Segmentation 论文阅读笔记

RIS 系列 Mask Grounding for Referring Image Segmentation 论文阅读笔记 一、Abstract二、引言三、相关工作Architecture Design for RISLoss Design for RISMasked Language Modeling 四、方法4.1 结构4.2 Mask Grounding讨论 4.3 跨模态对齐模块4.4 跨模态对齐损失4.5 损失…

顺序表基本操作实现

#include <stdio.h>#define MAX_SIZE 100// 定义顺序表的元素类型 typedef int ElementType;// 定义顺序表结构体 typedef struct {ElementType data[MAX_SIZE];int length; } SeqList;// 初始化顺序表 void InitList(SeqList *L) {L->length 0; }// 插入操作 int Li…

BP网络识别26个英文字母matlab

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;字母识别 获取完整源码源工程文件 一、 设计思想 字符识别在现代日常生活的应用越来越广泛&#xff0c;比如车辆牌照自动识别系统&#xff0c;手写识别系统&#xff0c;办公自动化等等。本文采用BP网络对26个英文字母进行…

优化小地图(非RawImage方法,节省性能)

优化小地图&#xff08;非RawImage方法&#xff0c;节省性能&#xff09; 一、小地图设计二、功能实现1.截取俯视图2.创建Cube包裹住场地&#xff0c;并且创建一个子物体坐标为&#xff08;0,0,0&#xff09;**3.创建UI显示小地图坐标转换代码如下&#xff1a; 一、小地图设计 …

我是如何转行 AI 并且实现薪资翻倍的

大家好啊&#xff0c;我是董董灿。 熟悉我的小伙伴都知道&#xff0c;我之前在北京某211大学&#xff0c;本硕读了7年的机械专业&#xff0c;后来硕士毕业后&#xff0c;果断转行去做了嵌入式开发&#xff0c;随后瞅准了 AI 爆发的时机果断转行去做了AI。 这段经历已经过去了…

【python与机器学习3】,感知机和与非门

1 电子和程序里的与门&#xff0c;非门&#xff0c;或门&#xff0c;与非门 &#xff0c;或非门&#xff0c;异或门 1.1 基础电路 与门&#xff08;AND gate&#xff09;、或门&#xff08;OR gate&#xff09;和非门&#xff08;NOT gate&#xff09;是数字逻辑电路中的三种基…

本地搭建【文档助手】大模型版(LangChain+llama+Streamlit)

概述 本文的文档助手就是&#xff1a;我们上传一个文档&#xff0c;然后在对话框中输入问题&#xff0c;大模型会把问题的答案返回。 安装步骤 先下载代码到本地 LangChain调用llama模型的示例代码&#xff1a;https://github.com/afaqueumer/DocQA&#xff08;代码不是本人…

自动驾驶规划算法

本文将讲解BFS&#xff0c;Dijstra&#xff0c;A*&#xff0c;动态规划的算法原理&#xff0c;不正之处望读者指正&#xff0c;希望有兴趣的读者能在评论区提出一些这些算法的面试考点&#xff0c;共同学习&#xff0c;一起进步 0 图论基础 图有三种&#xff1a;无向图、有向…

SRE 与 DevOps:你知道它们之间区别吗?

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享 DevOps专注于消除阻碍开发和运维之间协作的隔阂&#xff0c;而SRE致力于设计和实施可扩展、可靠的系统&#xff0c;确保最大可靠性。 这篇文章将探讨DevOps和SRE之间的差异&#xff0c…

Podman配置mongodb

文章目录 查询镜像拉取镜像查看镜像运行容器创建root用户 查询镜像 podman search mongo拉取镜像 podman pull docker.io/library/mongo查看镜像 podman images运行容器 podman run -d -p 27017:27017 --namemongodb-test docker.io/library/mongo创建root用户 podman exe…

SSH秘钥登录服务器

一、查看本机 ssh 公钥&#xff0c;生成公钥 1.通过命令窗口 a. 打开你的 git bash 窗口 b. 进入 .ssh 目录&#xff1a;cd ~/.ssh c. 找到 id_rsa.pub 文件&#xff1a;ls d. 查看公钥&#xff1a;cat id_rsa.pub 或者 vim id_rsa.pub git–查看本机 ssh 公钥&#xff0c…

小白--将笔记本上的代码或者项目上传到github上去教程(使用git命令)

文章目录 一、操作教程二、常见问题1. 问题12. 问题2 三、注意 一、操作教程 https://blog.csdn.net/Elon15/article/details/125705706?ops_request_misc%257B%2522request%255Fid%2522%253A%2522170340591716800215092652%2522%252C%2522scm%2522%253A%252220140713.130102…