JWT跨域认证解决方案

news2024/12/27 0:54:31

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理和用法。
在这里插入图片描述

一、跨域认证的问题

互联网服务离不开用户认证
一般流程是:

1、用户向服务器发送用户名和密码

2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间

3、服务器向用户返回一个 session_id,写入用户的 Cookie

4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器

5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份

这种模式的问题在于,扩展性(scaling)不好。
单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。

举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

  • 一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。

  • 另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。

总结:

什么是JWT?
JSON Web Token(JWT)是一个非常轻巧的规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

二、JWT 的原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,如:

{

“姓名”: “张三”,

“角色”: “管理员”,

“到期时间”: “2018年7月1日0点0分”

}

用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。

服务器就不保存任何 session 数据了,服务器变成无状态,从而比较容易实现扩展。

三、JWT 的数据结构

实际的 JWT 大概就像下面这样。

在这里插入图片描述

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。

JWT 的三个部分依次如下。

  • Header(头部) 明文

  • Payload(负载) 明文

  • Signature(签名)

写成一行,就是下面的样子。

Header.Payload.Signature

在这里插入图片描述

下面依次介绍这三个部分:

3.1 Header(头部)

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

{  "alg": "HS256",  "typ": "JWT"}

alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);
typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT

最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串。

在头部指明了签名算法是HS256算法。 我们进行BASE64编码https://base64.us/,https://www.zxgj.cn/g/base64编码后的字符串如下:

eyAgImFsZyI6ICJIUzI1NiIsICAidHlwIjogIkpXVCJ9

Base64是一种基于64个可打印字符来表示二进制数据的表示方法
由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符;三个字节有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示
JDK 中提供了非常方便的 BASE64EncoderBASE64Decoder,用它们可以非常方便的完成基于 BASE64 的编码和解码

3.2 Payload (载荷)

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用

iss (issuer):签发人
exp (expiration time):过期时间 这个过期时间必须要大于签发时间
sub (subject):主题 ==》jwt所面向的用户
aud (audience):受众 ==》接收jwt的一方
nbf (Not Before):生效时间 ==》(定义在什么时间之前,该jwt都是不可用的.iat (Issued At):签发时间
jti (JWT ID):编号 ==》(jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。)

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。

{"sub":"1234567890","name":"John Doe","admin":true}

JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

这个 JSON 对象也要使用 Base64URL 算法转成字符串,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

3.3 Signature (签证)

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)

  • payload (base64后的)

  • secret

签名,利用“加密算法”对JWT进行签名,保证没有被篡改过,值得注意的是,这里的数据都是明文的,算法实际上执行的是最后的数据签名功能,只能保证“不被篡改”,而不是保证“不被解密”,所以后面看到“加密、解密”,其实都是为签名服务的。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload), secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

3.4 Base64URL

Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+/=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法。

3.5 Jwt官网验证

为了验证上述生成的 jwt 是否合法,我们可以登录 JWT 官网,官网界面提供 JWT 解密功能,将生成好的 JWT 复制到如下图中进行解密

在这里插入图片描述

四、小小实验室

通过上述的介绍,我们已经了解到什么是 JWT 以及 JWT 生成的规则,现在我们通过代码方式来生成 JWT。

JWT 官网提供了通过不同编程语言来创建 JWT 的工具类/库

java-jwt

4.1 对称签名

首先引入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

生成JWT的token

@Test
    public void testGenerateToken(){
        // 指定token过期时间为10秒
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND, 60);

        String token = JWT.create()
                .withHeader(new HashMap<>())  // Header
                .withClaim("userId", 21)  // Payload
                .withClaim("userName", "baobao")
                .withExpiresAt(calendar.getTime())  // 过期时间
                .sign(Algorithm.HMAC256("!34ADAS"));  // 签名用的secret

        System.out.println(token);
    }

在这里插入图片描述

注意多次运行方法生成的token字符串内容是不一样的,尽管我们的payload信息没有变动。因为JWT中携带了超时时间,所以每次生成的token会不一样,我们利用base64解密工具可以发现payload确实携带了超时时间https://www.zxgj.cn/g/base64

在这里插入图片描述

解析JWT字符串

@Test
    public void testResolveToken(){
        // 创建解析对象,使用的算法和secret要与创建token时保持一致
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("!34ADAS")).build();
        // 解析指定的token
        DecodedJWT decodedJWT = jwtVerifier.verify(
                "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImJhb2JhbyIsImV4cCI6MTY4MTkxNzI2OSwidXNlcklkIjoyMX0.v07T_9QQ3b5nWHaO-oLWthekMyZXu7W9R8IOz2w9bk8"
        );
                // 获取解析后的token中的payload信息
        Claim userId = decodedJWT.getClaim("userId");
        Claim userName = decodedJWT.getClaim("userName");
        System.out.println(userId.asInt());
        System.out.println(userName.asString());
        // 输出超时时间
        System.out.println(decodedJWT.getExpiresAt());
    }

运行后发现报异常,原因是之前生成的token已经过期

在这里插入图片描述

再运行一次生成token的方法,改一下时间然后在过期时间60秒之内将生成的字符串拷贝到解析方法中,运行解析方法即可成功

在这里插入图片描述

4.2 封装工具类

可以将上述方法封装成工具类

public class JwtUtils {
        // 签名密钥
        private static final String SECRET = "!DAR$";

        /**
         * 生成token
         * @param payload token携带的信息
         * @return token字符串
         */
        public static String getToken(Map<String,String> payload){
            // 指定token过期时间为7天
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, 7);

            JWTCreator.Builder builder = JWT.create();
            // 构建payload
            payload.forEach((k,v) -> builder.withClaim(k,v));
            // 指定过期时间和签名算法
            String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SECRET));
            return token;
        }


        /**
         * 解析token
         * @param token token字符串
         * @return 解析后的token
         */
        public static DecodedJWT decode(String token){
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            return decodedJWT;
        }
    }

jjwt-root

4.3 对称签名

引入依赖

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

整合使用:

1、创建mapper文件

@Mapper
public interface UserMapper {
    Admin findByUserNameAndPassword(
            @Param("username") String username,
            @Param("password") String password);
}

2、构建Jwt工具类

@Component
public class JwtUtil {

    @Value("${jwt.secretKey}")
    private String secretKey;

    public String createJWT(String id, String subject, long ttlMillis, Map<String, Object> map) throws Exception {
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject) // 发行者
                .setIssuedAt(new Date()) // 发行时间
                // HS256算法实际上就是MD5加盐值,此时secretKey就代表盐值
                .signWith(SignatureAlgorithm.HS256, secretKey) // 签名类型 与 密钥
                .compressWith(CompressionCodecs.DEFLATE);// 对载荷进行压缩

        if (!CollectionUtils.isEmpty(map)) {
            builder.setClaims(map);
        }
        if (ttlMillis > 0) {
            builder.setExpiration(new Date(System.currentTimeMillis() + ttlMillis));
        }
        return builder.compact();
    }
        //获取有效的Jwttoken
        public Claims parseJWT(String jwtString) {
            return Jwts.parser().setSigningKey(secretKey)
                    .parseClaimsJws(jwtString)
                    .getBody();
        }
}

3、yml中加入:

jwt:
  secretKey: ak47
  mapper-locations: classpath:mapper.*Mapper.xml

4、构建mapper.xml

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.by.mapper.UserMapper">
    <select id="findByUserNameAndPassword" resultType="com.by.pojo.Admin">
        select * from admin where username = #{username} and password = #{password}
    </select>
</mapper>

5、创建service接口及实现类

public interface UserService {
    public String login(String username, String password) throws Exception;
}

//实现类
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private JwtUtil jwtUtil;
    @Resource
    private UserMapper userMapper;
    @Override
    public String login(String username, String password) throws Exception {
        //登录验证
        Admin user = userMapper.findByUserNameAndPassword(username, password);
        if (user == null) {
            return null;
        }
        //如果能查出,则表示账号密码正确,生成jwt返回
        String uuid = UUID.randomUUID().toString().replace("-", "");
        HashMap<String, Object> map = new HashMap<>();
        map.put("username", user.getUsername());
        map.put("age", user.getAge());
        return jwtUtil.createJWT(uuid, "login subject", 0L, map);
    }
}

6、创建controller类

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public JwtResponse login(@RequestParam("username") String username,
                             @RequestParam("password") String password){
        String jwt = "";
        try {
            jwt = userService.login(username, password);
            return JwtResponse.success(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            return JwtResponse.fail(jwt);
        }
    }
}

7、postman测试:

在这里插入图片描述

注意:

  • jjwt在0.10版本以后发生了较大变化,pom依赖要引入多个

4.4 首先引入Maven依赖

    <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.10.7</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.10.7</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.10.7</version>
            <scope>runtime</scope>
        </dependency>

标准规范中对各种加密算法的secretKey的长度有如下要求:

  • HS256:要求至少 256 bits (32 bytes)

  • HS384:要求至少384 bits (48 bytes)

  • HS512:要求至少512 bits (64 bytes)

  • RS256 and PS256:至少2048 bits

  • RS384 and PS384:至少3072 bits

  • RS512 and PS512:至少4096 bits

  • ES256:至少256 bits (32 bytes)

  • ES384:至少384 bits (48 bytes)

  • ES512:至少512 bits (64 bytes)

在jjwt0.10版本之前,没有强制要求,secretKey长度不满足要求时也可以签名成功。但是0.10版本后强制要求secretKey满足规范中的长度要求,否则生成jws时会抛出异常

4.5 创建测试类

	@Test
	public void testJWT() {
		Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
		
		System.out.println("=============创建 JWT===========");
		Date now = new Date();
		JwtBuilder builder= Jwts.builder()
				                .setId(UUID.randomUUID().toString()) // 载荷-标准中注册的声明
				                .setSubject("admin") // 载荷-标准中注册的声明
				                .setIssuedAt(now) // 载荷-标准中注册的声明,表示签发时间
				                .claim("id", "123456") // 载荷-公共的声明
				                .claim("name", "MoonlightL") // 载荷-公共的声明
				                .claim("sex", "male") // 载荷-公共的声明
								.signWith(key); // 签证
		
		String jwt = builder.compact();
		System.out.println("生成的 jwt :" +jwt);
		
		System.out.println("=============解析 JWT===========");
		
		try {
			Jws<Claims> result = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt);
			// 以下步骤随实际情况而定,只要上一行代码执行不抛异常就证明 jwt 是有效的、合法的
			Claims body = result.getBody();
			
			System.out.println("载荷-标准中注册的声明 id:" + body.getId());
			System.out.println("载荷-标准中注册的声明 subject:" + body.getSubject());
			System.out.println("载荷-标准中注册的声明 issueAt:" + body.getIssuedAt());
			
			System.out.println("载荷-公共的声明的 id:" + result.getBody().get("id"));
			System.out.println("载荷-公共的声明的 name:" + result.getBody().get("name"));
			System.out.println("载荷-公共的声明的 sex:" + result.getBody().get("sex"));
			
		} catch (JwtException ex) { // jwt 不合法或过期都会抛异常      
		    ex.printStackTrace();
		}
	}
}
  • setIssuedAt用于设置签发时间

  • signWith用于设置签名秘钥

测试运行,输出如下:

=============创建 JWT===========
生成的 jwt :eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJhZjZlNjcwMS1kMjhjLTRmOTQtOWU3OS02OGNkNjIyZGQ0ZGEiLCJzdWIiOiJhZG1pbiIsImlhdCI6MTY4MTg4MjEwOCwiaWQiOiIxMjM0NTYiLCJuYW1lIjoiTW9vbmxpZ2h0TCIsInNleCI6Im1hbGUifQ.bxbx_dgrBa1XsebalUOoQ4daBvP0lMysaVAxFvyPzAY
=============解析 JWT===========
载荷-标准中注册的声明 id:af6e6701-d28c-4f94-9e79-68cd622dd4da
载荷-标准中注册的声明 subject:admin
载荷-标准中注册的声明 issueAt:Wed Apr 19 13:28:28 CST 2023
载荷-公共的声明的 id:123456
载荷-公共的声明的 name:MoonlightL
载荷-公共的声明的 sex:male

再次运行,会发现每次运行的结果是不一样的,因为我们的载荷中包含了时间。

4.6 10版本封装工具类

public class JwtUtils {
    // token时效:24小时
    public static final long EXPIRE = 1000 * 60 * 60 * 24;
    // 签名哈希的密钥,对于不同的加密算法来说含义不同
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHOsdadasdasfdssfeweee";

    /**
     * 根据用户id和昵称生成token
     * @param id  用户id
     * @param nickname 用户昵称
     * @return JWT规则生成的token
     */
    public static String getJwtToken(String id, String nickname){
        String JwtToken = Jwts.builder()
                .setSubject("baobao-user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .claim("id", id)
                .claim("nickname", nickname)
                // 传入Key对象
                .signWith(Keys.hmacShaKeyFor(APP_SECRET.getBytes(StandardCharsets.UTF_8)), SignatureAlgorithm.HS256)
                .compact();
        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken token字符串
     * @return 如果token有效返回true,否则返回false
     */
    public static Jws<Claims> decode(String jwtToken) {
        // 传入Key对象
        Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor(APP_SECRET.getBytes(StandardCharsets.UTF_8))).build().parseClaimsJws(jwtToken);
        return claimsJws;
    }
}

五、JWT 的几个特点

(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

(3)JW 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证.

(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

六 、实际开发中的应用

在实际的SpringBoot项目中,一般我们可以用如下流程做登录:

  1. 在登录验证通过后,给用户生成一个对应的随机token(注意这个token不是指jwt,可以用uuid等算法生成),然后将这个token作为key的一部分,用户信息作为value存入Redis,并设置过期时间,这个过期时间就是登录失效的时间

  2. 将第1步中生成的随机token作为JWT的payload生成JWT字符串返回给前端

  3. 前端之后每次请求都在请求头中的Authorization字段中携带JWT字符串

  4. 后端定义一个拦截器,每次收到前端请求时,都先从请求头中的Authorization字段中取出JWT字符串并进行验证,验证通过后解析出payload中的随机token,然后再用这个随机token得到key,从Redis中获取用户信息,如果能获取到就说明用户已经登录

6.1 拦截器定义:

public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String JWT = request.getHeader("Authorization");
        try {
            // 1.校验JWT字符串
            DecodedJWT decodedJWT = JWTUtils.decode(JWT);
            // 2.取出JWT字符串载荷中的随机token,从Redis中获取用户信息
            ...
            return true;
        }catch (SignatureVerificationException e){
            System.out.println("无效签名");
            e.printStackTrace();
        }catch (TokenExpiredException e){
            System.out.println("token已经过期");
            e.printStackTrace();
        }catch (AlgorithmMismatchException e){
            System.out.println("算法不一致");
            e.printStackTrace();
        }catch (Exception e){
            System.out.println("token无效");
            e.printStackTrace();
        }
        return false;
    }
}

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

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

相关文章

【SpringBoot】SpringBoot-Admin 服务监控 + 告警通知

简单介绍 Spring Boot Actuator 是 Spring Boot 自带的一个功能模块&#xff0c; 提供了一组已经开箱即用的生产环境下常用的特性和服务&#xff0c;比如应用程序的健康检查、信息暴露、度量收集、日志记录等。 在实际项目中&#xff0c;Actuator 可以帮助我们快速了解应用程序…

三维重建以及神经渲染中的学习(一)

三维重建以及神经渲染中的学习 公众号AI知识物语 本文内容为参加过去一次暑期课程学习时的笔记&#xff0c;浅浅记录下。 显示表征&#xff1a; 点云points&#xff1a;由一组离散三维点表征物体表面 推理速度快&#xff0c;容易获取 -离散表征&#xff0c;无拓扑关系 [Fa…

mysql的聚簇索引和非聚簇索引的区别

MySQL InnoDB存储引擎时&#xff0c;索引类型可分为聚簇索引和非聚簇索引&#xff0c;有时候也通俗的称为主键索引和普通索引。MyISAM的引擎只有非聚簇索引&#xff0c;所以MYSIAM的引擎在查询的时候非主键索引的时候特别快 接下来讨论一下几个问题&#xff1a; 什么是聚簇索…

linux搭建vsftpd服务使用filezilla连接服务

背景&#xff1a;支持使用filezilla上传文件到公司的服务机器上&#xff0c;所以搭建vsftpd支持filezilla进行上传 一、linux机器搭建vsftpd服务 1、先看看自己的机器属于什么发行版&#xff0c;不同的发行版命令不一样 我的是centos&#xff0c;其他发行版命令不一样&#xf…

架构训练营笔记:高可用设计

2-3高可用设计 高可用复杂度模型 分为计算高可用&#xff0c;存储高可用&#xff0c;高可用本质上需要冗余&#xff0c;这里是集群&#xff0c;没有单机。 计算高可用&#xff1a;分为任务分配与任务分解。 计算高可用对比之前的高性能&#xff0c;就是多了状态检测。 任务…

请求响应-实体参数的接受

实体参数的接受 简单实体对象&#xff1a;请求参数名与形参属性对象名相同&#xff0c;定义pojo接受即可&#xff0c;将数据封装到实体类中实体类代码如下&#xff1a; package com.example.POJO;public class User {private String name;private Integer age;public String g…

系统测试——postman的400错误

如果Headers中不勾选Host&#xff0c;调用接口就会报400 Bad Request错误。

Hystrix 断路器

文章目录 1 问题&#xff1a;服务雪崩2 概念3 服务降级3.1 概念&#xff1a;3.2 触发服务降级的情况&#xff1a;3.3 应用3.3.1 依赖3.3.2 解决的问题3.3.3 生产者&#xff1a;3.3.4 消费者&#xff1a;3.3.5 配置全局fallback方法3.3.6 解耦合 4 服务熔断4.1 概念&#xff1a;…

Shell的条件运算语句

目录 IF语句 单分支语句语法语法 多分支结构语法 CASE语句 语法 IF语句 单分支语句语法语法 #写法1 if 条件语句 then内容 fi#写法2 if 条件语句 ;then内容 fi编写一个内容警报器的例子 #!/bin/bash free_mem$(free -m | grep "Mem:" | tr -s " "…

kubernetes源码学习之kube-scheduler

kube-scheduler是kubernetes中的调度程序&#xff0c;负责从api server中获得待分发的pod列表&#xff0c;并为他们找到最合适运行的Node。 基于kubernetes 1.27 基本框架 下面是kubernetes官发给出的框架图&#xff0c;先对kubernetes pod调度的大致流程有一个认识 看一下有…

眼睛:来一场视觉盛宴《手拿把掐》css特效 —— 之听说过CSS【笑】

&#x1f637;&#x1f60a;&#x1f93a;&#x1f93a;&#x1f93a;前期回顾 打造极简风格动效 —— 5 分钟轻松实现惊艳、震撼人心的视觉效果_彩色之外的博客-CSDN博客 &#x1f601; css动画 —— 把你喜欢css动画嵌入到浏览器中_css做的动画效果怎么嵌入网页_彩色之外的…

window电脑修复网络不能正常

问题描述 问题的起点是我打开了OpenAPI公司的GPT&#xff0c;在回答的过程中响应很慢&#xff0c;然后自己开始尝试切换连接的服务器&#xff08;这里使用到了网络代理&#xff09;&#xff0c;最后自己做了一个操作是 代理软件的这个菜单里面的增强模式选项&#xff0c;结果…

Android爬坑指南————工信部又出新规!

工信部又出新规了&#xff01; 一、背景二、整改2.1 个人信息保护2.1.1 基本模式&#xff08;无权限、无个人信息获取模式&#xff09;腾讯视频网易云音乐 2.1.2 隐私政策内容 2.2 app权限调用2.2.1 应用内权限调用2.2.1.1 获取定位信息和生物特征识别信息2.2.1.2 其他权限 2.3…

渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?

在上一篇文章中我们介绍了导航相关的流程&#xff0c;那导航被提交后又会怎么样呢&#xff1f; 就进入了渲染阶段。这个阶段很重要&#xff0c;了解其相关流程能让你“看透”页面是如何工作的&#xff0c;有了这些知识&#xff0c;你可以解决一系列相关的问题&#xff0c;比如…

SVR算法简介及与其它回归算法的关系

目录 参考链接 有人可以帮助我理解支持向量回归技术和其他简单回归模型之间的主要区别是什么 支持向量回归找到一个线性函数&#xff0c;表示误差范围 (epsilon) 内的数据。也就是说&#xff0c;大多数点都可以在该边距内找到&#xff0c;如下图所示 这意味着 SVR 比大多数其…

TypeScript 学习笔记(一):基本类型、交叉类型、联合类型、类型断言

文章目录 一、常见类型1. 数组2. 布尔3. 数值4. 字符串5. object6. null 和 undefined7. symbol7.1 作为属性名7.2 属性名遍历7.3 静态方法&#xff1a;Symbol.for()和 Symbol.keyFor()7.4 内置 symbol 值7.4.1 Symbol.hasInstance7.4.2 Symbol.isConcatSpreadable7.4.3 Symbol…

Android 报错,闪退(错误)日志保存到手机内存中,以文本文件的形式保存

1.直接贴代码 import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Environment; import android.util.Log;import com.nuotu.atmBookClient.App;import java.io.File; i…

python接口自动化(三十二)--Python发送邮件(常见四种邮件内容)番外篇——上(详解)

简介 本篇文章与前边没有多大关联&#xff0c;就是对前边有关发邮件的总结和梳理。在写脚本时&#xff0c;放到后台运行&#xff0c;想知道执行情况&#xff0c;会通过邮件、SMS&#xff08;短信&#xff09;、飞信、微信等方式通知管理员&#xff0c;用的最多的是邮件。在linu…

这份4577页的Java面试PDF,让我成功斩获阿里、字节等大厂offer

我为大家准备了一份超级全面的Java 学习面试笔记&#xff0c;这份电子版笔记涵盖了诸多后端技术栈的面试题和答案&#xff0c;相信可以帮助大家在最短的时间内复习Java后端的大多数技术点和面试题&#xff0c;从而拿到自己心仪的offer。共4577页。整体还是比较清爽的&#xff0…

Postman的细节回顾

之前在学校摸索着玩过postman&#xff0c;工作后要使用postman&#xff0c;发现对于很多细节&#xff0c;这里补充说明一下&#xff0c;当作使用手册。 之所以使用postman&#xff0c;是因为更便捷的查看接口情况&#xff0c;不需要每次在浏览器f12查看。 目录 1 创建请求2 测…