登录认证(全集)--jwt,Filter,Interceptor详细说明

news2025/1/14 1:11:51

一、登录校验

  • 问题:在未登录情况下,我们也可以直接访问部门管理、员工管理等功能。
  • 由于浏览器与web服务器中的数据交互是通过HTTP协议的,而HTTP协议是无状态的–即每个页面中的请求和响应都是独立的,没有状态存在。
  • 所以我们需要进行登录校验

1.登录校验

  • 每次访问页面的时候可以用if...else...来进行判断用户是否登录。但过程较为繁琐,所以我们设置统一拦截。

二、统一拦截

1.登录标记

(1).会话技术:

  • 用户登录成功之后,每一次请求中,都可以获取到该标记。
  1. 会话:
  • 用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束在一次会话中可以包含多次请求和响应。
  1. 会话跟踪:
  • 一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据

(2).会话跟踪方案:

  1. 客户端Cookie(传统)
  • 客户端会话跟踪技术:Cookie
import com.mannor.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@RestController
public class SessionController {
    //设置cookie
    @GetMapping("/c1")
    public Result cookie(HttpServletResponse response) {
        response.addCookie(new Cookie("login_username", "mannor"));//设置Cookie/响应Cookie
        return Result.success();

    }

    @GetMapping("/c2")
    public Result cookie2(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();//获取所有的Cookie
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals("login_username")) {//输出name为 login_username 的cookie
                System.out.println("login_username: " + cookie.getValue());
            }
        }
        return Result.success();
    }
}

优点:HTTP协议中支持
缺点:1.移动端APP无法使用cookie不安全,2.用户可以自己禁用,3.Cookiecookie不能跨域。

  1. 服务端Session(传统)
  • 服务端会话跟踪技术:Session,基于cookie开发
import com.mannor.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Slf4j
@RestController
public class SessionController {
    //往HttpSession中存储值
    @GetMapping("/s1")
    public Result session1(HttpSession session) {
        log.info("attpSession-s1: {}", session.hashCode());
        session.setAttribute("loginUser", "tom");//往session中存储数据
        return Result.success();
    }

    //从HttpSession中获取值
    @GetMapping("/s2")
    public Result session2(HttpServletRequest request) {
        HttpSession session = request.getSession();
        log.info("HttpSession-s2:{}", session.hashCode());
        Object loginUser = session.getAttribute("loginUser");//从session中获取数据
        log.info("loginUser: {}", loginUser);
        return Result.success(loginUser);
    }
}

优点:存储在服务器,安全性高
缺点:1.在服务器集群的情况下无法直接使用Session; 2.Cookie的缺点(基于Cookie开发)。

  1. JWT令牌技术 (主流)
//具体相关内容在下详细介绍

优点:1.支持PC端和移动端 2.解决了集群环境下的认证问题 3.减轻了服务器端存储的压力(不用存储)。
缺点:需要自己实现。

(3). JWT令牌技术

  1. 简介:
  • 全称:JSON Web Token 官网:https://jwt.io/
  • 定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
  1. 组成:
  • 第一部分: Header(头),记录令牌类型、签名算法等。例如: {"alg":"HS256", "type":"JwT"}
  • 第二部分: Payload(有效载荷),携带一些自定义信息、默认信息等。例如: {"id":"1","username":"Tom"}
  • 第三部分: Signature(签名),防止Token被篡改、确保安全性。将headerpayload,并加入指定秘钥,通过指定签名算法计算而来。

生成JWT令牌时要对JSON格式的数据进行Base64编码:一种基于64个可打印字符(A-Z a-Z 0-9 + /)来表示二进制数据的编码方式。

//原始的JWT令牌:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
//1.HEADER:ALGORITHM & TOKEN TYPE  (头)
{
  "alg": "HS256",
  "typ": "JWT"
}
//2.PAYLOAD:DATA  (有效载荷)
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}
//3.VERIFY SIGNATURE  (数字签名)--通过前面的数据,执行签名算法
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),

) 
  1. 应用场景
  • 场景:登录认证。
    • 登录成功后,生成令牌
    • 后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理
  1. JWT-生成
  • 首先需要引入pom.xml依赖:
<!-- JWT令牌-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
  • 创建一个测试类
/**
 * 生成JWT令牌
 */
@Test
public void testGenJwt() {
    //利用Map集合来整合需要存储的数据
    Map<String, Object> claims = new HashMap<>();
    claims.put("id", 1);
    claims.put("name", "tom");
    //链式编程
    String jwt = Jwts.builder()
            .signWith(SignatureAlgorithm.HS256, "mannor")//签名算法  解析密钥
            .setClaims(claims)//自定义内容(载荷)
            .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置有效期为1h
            .compact();
    System.out.println(jwt);
	//控制台输出:eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY4NzQ4OTkxOH0.FmNSF7KDVZ9SaGoVJb6jNDq_oqlgSeUcFBskGNgy0UE
	
}
  • 把输出的JWT令牌发放到官网的Encoded下可以看见原始数据
  1. JWT-解析
/**
 * 解析JwT
 */
@Test
public void testParseJwt() {
    Claims claims = Jwts.parser()
            .setSigningKey("mannor") //解析密钥
            .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY4NzQ5MTIwN30.Tjzr4CKzwJcGtYwiRCo7d71z6WNugMMefu09hUp5H3o")
            .getBody();
    System.out.println(claims);
}
  • JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
  • 如果JWT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法。

(4).JWT的登录校验实现(登录-生成令牌)

  1. 步骤:
  • 引入JWT令牌操作工具类。
  • 登录完成后,调用工具类生成JWT令牌,并返回。
  1. 编写一个Util工具类:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;

public class JwtUtils {

    private static String signKey = "mannor"; //登录令牌
    private static Long expire = 43200000L;  //十二个小时后过期

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}
  1. 重新编写Controller层代码:
import com.mannor.Service.EmpService;
import com.mannor.pojo.Emp;
import com.mannor.pojo.Result;
import com.mannor.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        log.info("登录的查询参数:{},{}", emp.getUsername(), emp.getPassword());
        Emp e = empService.loginSelect(emp);

        //登陆成功生成令牌,下发令牌
        if (e != null) {
            Map<String, Object> claim = new HashMap<>(); //存如我们需要的信息,ID,name、username
            claim.put("id", e.getId());
            claim.put("name", e.getName());
            claim.put("username", e.getUsername());
            String jwt = JwtUtils.generateJwt(claim);//jwt中包含了员工当前的登录信息
            return Result.success(jwt);
        }
        //登录失败,返回错误信息
        return Result.error("用户名或密码错误,请检查重新输入");
    }
}
  • 剩下的就需要拦截器来处理

2.统一拦截–过滤器Filter

1. 概述

  • 概念: Filter过滤器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。
  • 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
  • 过滤器一般完成一些通用的操作,比如:登录校验统一编码处理敏感字符处理等。

2. Filter的快速入门

  • 1.定义Filter:定义一个类,实现Filter接口,并重写其所有方法。
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")  //这是JavaWeb中的组件,springboot中没有,还需要在启动类上加上@ServletComponentScan注解
public class demoFilter implements Filter {

    @Override  //初始化、只调用一次 项目启动自动调用
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init初始化方法");
    }

    @Override   
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("拦截到了请求");

        //拦截放行,在路径资源访问之后
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override  //销毁方法,也只调用一次 项目结束自动调用
    public void destroy() {
        System.out.println("destroy初始化方法");
    }
}
  • 2.配置Filter: Filter类上加@WebFilter注解,配置拦截资源的路径。启动类上加@ServletComponentScan开启Servlet组件支持。

3. Filter详解

  • 1.Filter可以根据需求,配置不同的拦截资源路径:
拦截路径urlPatterns值含义
拦截具体路径/login只有访问/login 路径时,才会被拦截
目录拦截/emps/*访问/emps下的所有资源,都会被拦截
拦截所有/*访问所有资源,都会被拦截

4. 过滤器链

  • 介绍:一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链
  • 执行流程:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wHREKRhw-1687578908342)(18_files/1.jpg)]
  • 执行顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。

5. 利用Filter过滤器实现登录校验

  1. 流程
  • 获取请求url。
  • 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
  • 获取请求头中的令牌(token) 。
  • 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
  • 解析token,如果解析失败,返回错误结果(未登录)。
  • 放行。
    在这里插入图片描述
  1. 代码实现:(具体细节请看注释)
import com.alibaba.fastjson.JSONObject;
import com.mannor.pojo.Result;
import com.mannor.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        //1. 获取请求url。
        String requestURI = req.getRequestURI();
        log.info("请求的url:{}", requestURI);

        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if (requestURI.contains("login")) {
            log.info("登录操作");
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        //3.获取请求头中的令牌(token)。
        String jwt = req.getHeader("token");

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if (!StringUtils.hasLength(jwt)) {
            log.info("请求头token为空,返回未登录的信息");
            Result error = Result.error("NOT_LOGIN");//前端需要响应一个json格式的数据
            //手动将对象转换为json格式的数据-------》使用阿里巴巴fastJSON的工具包(需要引入依赖)
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);//使用HttpServletResponse对象返回错误的JSON数据
            return;
        }
        //5.解析token(校验jwt令牌),如果解析失败,返回错误结果(未登录)。如果错误会报错,所以使用try...catch...
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//程序执行到这里说明程序执行失败--jwt令牌错误
            e.printStackTrace();
            log.info("解析令牌失败,返回未登录的错误信息");
            Result error = Result.error("NOT_LOGIN");//前端需要响应一个json格式的数据
            //手动将对象转换为json格式的数据-------》使用阿里巴巴fastJSON的工具包(需要引入依赖)
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);//使用HttpServletResponse对象返回错误的JSON数据
            return;
        }
        //6.放行。
        log.info("令牌合法,直接放行");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}


3.统一拦截–拦截器Interceptor

1.概述

  • 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。
  • 作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
  • 与过滤器Filter的使用差不多。

2.快速入门

  1. 定义拦截器,实现HandlerInterceptor接口,并重写其所有方法。
package com.mannor.Interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class LoinCheckInterceptor implements HandlerInterceptor {

    @Override  //目标资源方法运行前运行,返回true:放行;false:不放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...运行了");
        return true;
    }

    @Override   //目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...运行了");
    }

    @Override   //视图渲染完毕后运行,最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...运行了");
    }
}
  1. 注册配置拦截器
package com.mannor.Config;

import com.mannor.Interceptor.LoinCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration   //声明为配置类
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoinCheckInterceptor loinCheckInterceptor;
	
    @Override  //用来注册拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loinCheckInterceptor).addPathPatterns("/**");
    }
}

3.拦截路径

  • 拦截器可以根据需求,配置不同的拦截路径:
拦截路径含义举例
/*一级路径能匹配/depts,/emps,/login,不能匹配/depts/1
/**任意级路径能匹配/depts,/depts/1 ,/depts/1/2
/depts/*/depts下的一级路径能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/**/depts下的任意级路径能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1
  • addPathPatterns:拦截的路径。
  • excludePathPatterns:不拦截的路径。
@Configuration   //声明为配置类
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoinCheckInterceptor loinCheckInterceptor;

    @Override  //用来注册拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loinCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
    }
}

4.拦截器-执行流程

  • 过滤器和拦截器的执行先后流程:
    在这里插入图片描述

5.Filter与Interceptor

  • 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
  • 拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源

6.登录校验的实现–Interceptor

  1. 步骤(与Filter一样)
  • 获取请求url。
  • 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
  • 获取请求头中的令牌( token) 。
  • 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
  • 解析token,如果解析失败,返回错误结果(未登录)。
  • 放行。
    在这里插入图片描述
package com.mannor.Interceptor;

import com.alibaba.fastjson.JSONObject;
import com.mannor.pojo.Result;
import com.mannor.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Slf4j
@Component
public class LoinCheckInterceptor implements HandlerInterceptor {

    @Override  //目标资源方法(Controller方法)运行前运行,返回true:放行;false:不放行
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        System.out.println("preHandle...运行了");
        //1. 获取请求url。
        String requestURI = req.getRequestURI();
        log.info("请求的url:{}", requestURI);

        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if (requestURI.contains("login")) {
            log.info("登录操作");
            return true;
        }
        //3.获取请求头中的令牌(token)。
        String jwt = req.getHeader("token");

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if (!StringUtils.hasLength(jwt)) {
            log.info("请求头token为空,返回未登录的信息");
            Result error = Result.error("NOT_LOGIN");//前端需要响应一个json格式的数据
            //手动将对象转换为json格式的数据-------》使用阿里巴巴fastJSON的工具包(需要引入依赖)
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);//使用HttpServletResponse对象返回错误的JSON数据
            return false;
        }
        //5.解析token(校验jwt令牌),如果解析失败,返回错误结果(未登录)。如果错误会报错,所以使用try...catch...
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//程序执行到这里说明程序执行失败--jwt令牌错误
            e.printStackTrace();
            log.info("解析令牌失败,返回未登录的错误信息");
            Result error = Result.error("NOT_LOGIN");//前端需要响应一个json格式的数据
            //手动将对象转换为json格式的数据-------》使用阿里巴巴fastJSON的工具包(需要引入依赖)
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);//使用HttpServletResponse对象返回错误的JSON数据
            return false;
        }
        //6.放行。
        log.info("令牌合法,直接放行");
        return true;
    }

    @Override   //目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...运行了");
    }

    @Override   //视图渲染完毕后运行,最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...运行了");
    }
}

除此之外,还需要在配置类WebConfig.java中使用excludePathPatterns()方法对cssvuejs等静态资源放行,不然Interceptor会产生拦截

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

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

相关文章

WordPress网站的robots文件代码怎样写比较好

本文记载和介绍的是wordpress的robots.txt的在哪儿&#xff1f;以及如何修改robots文件来禁止搜索引擎爬取/ajax目录下的文章。以及如何优化WordPress网站的robots文件 wordpress设置robots文件的目录&#xff1a;/wp-includes/functions.php&#xff0c;然后搜索do_robotstxt…

【雕爷学编程】Arduino动手做(123)---Multi-function扩展板

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

随机数检测(一)

随机数检测&#xff08;一&#xff09;- 随机数自测试概述 1 概述2 产品形态划分和检测项目3 测试方式3.1 概述3.2 单比特频数检测方法3.3块内频数检测方法 1 概述 随机数发生器设计完成后&#xff0c;使用随机数发生器的产品需对其执行测试&#xff0c;防止应用过程中产生不合…

Redis的数据类型及对应的数据结构(二)

接上篇&#xff1a;Redis的数据类型及对应的数据结构&#xff08;一&#xff09;_鱼跃鹰飞的博客-CSDN博客 本篇主要讨论剩下的几种数据结构的应用场景 应用场景 集合的主要几个特性&#xff0c;无序、不可重复、支持并交差等操作。 因此 Set 类型比较适合用来数据去重和保…

卧剿,6万字!30个方向130篇!CVPR 2023 最全 AIGC 论文!一口气读完。

一杯奶茶&#xff0c;成为 AIGCCV 视觉前沿弄潮儿&#xff01; 25个方向&#xff01;CVPR 2022 GAN论文汇总 35个方向&#xff01;ICCV 2021 最全GAN论文汇总 超110篇&#xff01;CVPR 2021 最全GAN论文梳理 超100篇&#xff01;CVPR 2020 最全GAN论文梳理 在最新的视觉顶会 C…

Java多重捕获块

try块后面可以跟随一个或多个catch块。每个catch块必须包含一个不同的异常处理程序。因此&#xff0c;如果您在不同的异常发生时需要执行不同的任务&#xff0c;请使用Java多重捕获块。 需要记住的要点 一次只会发生一个异常&#xff0c;同时只有一个catch块被执行。 所有的ca…

实训五:数据库安全控制 - MySQL数据库 - 授权与撤销授权

MySQL数据库 - 授权与撤销授权 第1关&#xff1a;数据库授权任务描述相关知识用户管理授权方式 编程要求测试说明参考代码 第2关&#xff1a;数据库撤销权限任务描述相关知识撤销权限 编程要求测试说明参考代码 第1关&#xff1a;数据库授权 任务描述 本关任务&#xff1a;新…

Ubuntu安装Anaconda详细步骤(Ubuntu22.04.1,Anaconda3-2023.03)

本文主要讲述了在Ubuntu中安装anaconda的具体步骤 准备环境&#xff1a;Ubuntu22.04.1&#xff0c;Anaconda3 1.下载Anaconda3 在清华镜像下载Linux版本的anaconda 清华镜像官网Anaconda下载 我选择的是Anaconda3-2023.03-Linux-x86_64.sh 下载好的Anaconda3-2023.03-Linux-…

拦截器Interceptor及与过滤器Filter的区别

统一拦截–拦截器Interceptor 1.概述 概念:是一种动态拦截方法调用的机制&#xff0c;类似于过滤器。Spring框架中提供的&#xff0c;用来动态拦截控制器方法的执行。作用:拦截请求&#xff0c;在指定的方法调用前后&#xff0c;根据业务需要执行预先设定的代码。与过滤器Fil…

【Java|多线程与高并发】设计模式-单例模式(饿汉式,懒汉式和静态内部类)

文章目录 1. 前言2. 单例模式3. 如何保证一个类只有一个实例4. 饿汉式单例模式5. 懒汉式单例模式6. 实现线程安全的懒汉式单例7. 静态内部类实现单例模式8. 总结 1. 前言 设计模式是一种在软件开发中常用的解决复杂问题的方法论。它提供了一套经过验证的解决方案&#xff0c;用…

通信原理概述

通信是指人们通过某种媒介进行信息传递。通过某种介质或通道&#xff0c;将信息从一个地点传递到另一个地点的过程。通信可以是人与人之间的交流&#xff0c;也可以是设备、系统或网络之间的数据传输。 通信信号的分类方法&#xff1a; 1&#xff09;模拟信号和数字信号(从信号…

数字游戏:如何统计能整除数字的位数?

本篇博客会讲解力扣“2520. 统计能整除数字的位数”的解题思路&#xff0c;这是题目链接。 本题的思路是&#xff1a;取出每一位&#xff0c;判断是否能整除。 如何取出每一位呢&#xff1f;可以采用“mod10除10”的策略。即&#xff1a;每次mod10取出最后一位数&#xff0c;再…

2022年软件测试面试题大全【含答案】

一、面试基础题 简述测试流程: 1、阅读相关技术文档&#xff08;如产品PRD、UI设计、产品流程图等&#xff09;。 2、参加需求评审会议。 3、根据最终确定的需求文档编写测试计划。 4、编写测试用例&#xff08;等价类划分法、边界值分析法等&#xff09;。 5、用例评审(…

C++面向对象 this指针 构造函数 析构函数 拷贝构造 友元

C面向对象 面向对象概念类与对象的区别 C中类的设计设计实例实例解释共有和私有类的认识 函数定义函数在类里定义和类外定义区别函数定义实例 C对象模型方案一:各对象完全独立地安排内存的方案方案二:各对象的代码区共用的方案: this指针this指针特点程序编译面向对象程序的过程…

【力扣刷题 | 第十四天】

目录 前言&#xff1a; 7. 整数反转 - 力扣&#xff08;LeetCode&#xff09; 面试题 16.05. 阶乘尾数 - 力扣&#xff08;LeetCode&#xff09; 总结; 前言&#xff1a; 今天仍然是无固定类型刷题&#xff0c; 7. 整数反转 - 力扣&#xff08;LeetCode&#xff09; 给你…

傅氏变换算法

半局积分算法的局限性是要求采样的波形为正弦波。当被采样的模拟量不是正弦波而是一个周期性时间函数时&#xff0c;可采用傅氏变换算法。傅氏变换算法来自于傅里叶级数&#xff0c;即一个周期性函数I&#xff08;t&#xff09;可用傅里叶级数展开为各次谐波的正弦项和余弦项之…

D117-72. 编辑距离

题目描述 链接&#xff1a;添加链接描述 跟只考虑删除的完全一样&#xff0c;只不过是dp[i-1][j-1]1 class Solution:def minDistance(self, word1: str, word2: str) -> int:# dp[i][j]&#xff1a;以i-1为结尾的字符串word1&#xff0c;和以j-1位结尾的字符串word2&…

Qt 将某控件、图案绘制在最前面的方法,通过QGraphicsScene模块实现

文章目录 前言一、效果二、代码实现1.工程文件夹结构2.BackWidget类2.1 backwidget.h2.2 backwidget.cpp 总结 前言 在用Qt做一些应用的时候&#xff0c;有可能遇到和“绘制顺序”相关的问题&#xff0c;即要控制一些控件之间的显示前后问题&#xff0c;在常用的QWidget体系中&…

【数据结构与算法】力扣:翻转二叉树

翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,…

云HIS是什么?HIS系统为什么要上云?云HIS有哪些优点?

一、当前医疗行业HIS的现状与发展趋势 1.医院信息系统&#xff08;HIS&#xff09;经历了从手工到单机再到局域网的两个阶段&#xff0c;随着云计算、大数据新技术迅猛发展&#xff0c;基于云计算的医院信息系统将逐步取代传统局域网HIS , 以适应人们对医疗卫生服务越来越高的要…