SpringSecurity深度学习

news2024/11/19 2:35:11

 SpringSecurity简介

spring Security是什么?

Spring Security 是一个强大且高度可定制的身份验证和访问控制框架,用于保护基于Spring的应用程序。它是Spring项目的一部分,旨在为企业级系统提供全面的安全性解决方案。

一个简单的授权和校验流程

检验流程

总流程 

SpringSecurity使用

认证 

对应依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

在自定义检验的时候,主要就是实现UserDetailsService接口,重写loadUserByUserName方法,在该方法中就是去检验账号和密码的准确性。(一般都是进行数据库的查询校验,默认的密码格式就是 ·{}密码·)

前后端分离项目登录流程

1.在springSecurity中我们的需要设置密文的配置,在项目中大多都是使 BCryptPasswordEncoder类来做密码的加密。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


}

2.创建对应的login接口和service

Controller 

@RestController
@RequestMapping("/user")
public class LoginController {

    @Autowired
    LoginService loginService;

    @PostMapping("/login")
    public ResponseResult login(@RequestBody User user) {
        return loginService.login(user);
    }
}

Service

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    RedisCache redisCache;
    @Override
    public ResponseResult login(User user) {
        //authenticationManager authenticate进行用户验证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        //执行我们对饮的认证方法,在该方法中会返回LoginUser类型的数据
        //如果没有通过通过直接抛异常
        if(ObjectUtil.isEmpty(authenticate)) {
            throw new RuntimeException("登录失败");
        }
        //如果成功直接生成token,将其也map返回
        LoginUser loginUser = (LoginUser)authenticate.getPrincipal();
        String jwt = JwtUtil.createJWT(loginUser.getUser().getId().toString());
        Map<String, String> data = new HashMap<>();
        data.put("token", jwt);
        redisCache.setCacheObject(loginUser.getUser().getId().toString(), user);
        //返回token
        return new ResponseResult(200, "登录成功", data);
    }
}

因为AuthenticationManager默认不在ioc中,我们需要将其配置到ioc中,并且配置对应的校验规则。在里面就包括 无效校验的接口(比如:登录接口)和其他一些前后端分离的配置。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //将AuthenticationManager配置到ioc中
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //配置权限规则,主要就睡要放行登录接口,不然登录接口都会被了解,以及其他不要的前后端分离的配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //由于是前后端分离项目,所以要关闭csrf
                .csrf().disable()
                //由于是前后端分离项目,所以session是失效的,我们就不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //指定让spring security放行登录接口的规则
                .authorizeRequests()
                // 对于登录接口 anonymous表示允许匿名访问
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();
    }
}

进行测试,校验成功。

前后端分离项目校验流程

1.创建一个校验过滤器

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取token,token会存在header中
        String token = request.getHeader("token");
        if(StrUtil.isEmpty(token)) {
            //由后续的拦截器进行拦截
            filterChain.doFilter(request, response);
            //后续会返回回来,需要return,不然会执行下面的语句
            return ;
        }
        //解析token
        String userId;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            userId = claims.getSubject();
            if(StringUtil.isNullOrEmpty(userId)) {
                throw new RuntimeException("token解析失败");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //从redis中获取用户的信息
        LoginUser loginUser = redisCache.getCacheObject(userId);
        if(ObjectUtil.isEmpty(loginUser)) {
            throw new RuntimeException("Redis中没有用户信息");
        }
        //将数据存储到SecurityContextHolder
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //放行
        filterChain.doFilter(request, response);
    }
}

使用三个参数的UsernamePasswordAuthenticationToken的构造器,该构造器会设置授权成功。

2.将过滤器设置到用户验证过滤器之前

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    //设置加密方式
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //将AuthenticationManager配置到ioc中
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //配置权限规则,主要就睡要放行登录接口,不然登录接口都会被了解,以及其他不要的前后端分离的配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //由于是前后端分离项目,所以要关闭csrf
                .csrf().disable()
                //由于是前后端分离项目,所以session是失效的,我们就不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //指定让spring security放行登录接口的规则
                .authorizeRequests()
                // 对于登录接口 anonymous表示允许匿名访问
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();


        //将过滤器添加到用户登录处理器之前
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

进行测试,将成功的token放入header中,进行校验。最终校验成功。

退出登录流程

1.编写退出登录接口

@RestController
@RequestMapping("/user")
public class LoginController {

    @Autowired
    LoginService loginService;


    @RequestMapping("/logout")
    public ResponseResult logout() {
        return loginService.logout();
    }
}

2.编写service实现类,删除redis中用户信息的数据,即可完成退出登录操作。在解析的时候redis中的数据不存在就会直接被拦截。

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    RedisCache redisCache;


    @Override
    public ResponseResult logout() {
        //在进入此接口时会先进行解析,成功之后才会执行logout,此时SecurityContextHolder中是有用户信息的
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        if(ObjectUtil.isEmpty(loginUser)) {
            throw new RuntimeException("LoginUser不存在");
        }
        //把redis中的数据删除之后,下次解析的时候就会直接报错,在解析中我们对redis的数据做了判空的操作
        redisCache.deleteObject(loginUser.getUser().getId().toString());
        return new ResponseResult(200, "退出登录成功", null);
    }
}

进行测试,最终成功。

授权

1.开启授权功能,在对应的security的配置类中添加对应的注解。

@EnableGlobalMethodSecurity(prePostEnabled = true) //开启授权

2.为接口设置对应的权限需求

@RestController
public class baseController {

    @RequestMapping("/hello")
    //拥有text倾向才能访问
    @PreAuthorize("hasAuthority('text')")
    public String hello() {
        return "hello!";
    }
}

3.在用户进行认证的时候查询用户拥有的权限集合,并设置到 authenticationToken中。

  • 在返回类型中设置权限集合属性和重写获取权限集合方法。
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {

    private User user;

    private List<String> authorities2String;

    public LoginUser(User user, List<String> authorities2String) {
        this.user = user;
        this.authorities2String = authorities2String;
    }

    @JSONField(serialize = false)
    private List<GrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if(CollUtil.isEmpty(authorities)) {
            return authorities2String.stream()
                    .map(item -> new SimpleGrantedAuthority(item))
                    .collect(Collectors.toList());
        }
            return authorities;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUserName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
  • 在校验Service中查询对应的权限列表。
@Service
public class UserService implements UserDetailsService {

    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<com.huang.springsecuritydemo.entity.User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("user_name", username);
        com.huang.springsecuritydemo.entity.User user = userMapper.selectOne(userQueryWrapper);
        if(ObjectUtil.isEmpty(user)) {
            throw new RuntimeException("用户不存在");
        }
        //todo 查询并设置对应的权限信息
        //模拟查到的权限信息
        List<String> data = Arrays.asList("test", "text");

        return new LoginUser(user, data);
    }
}
  • 在JWT认证中向authenticationToken中设置权限集合,最终设置到SecurityContextHolder中。
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取token,token会存在header中
        String token = request.getHeader("token");
        if(StrUtil.isEmpty(token)) {
            //由后续的拦截器进行拦截
            filterChain.doFilter(request, response);
            //后续会返回回来,需要return,不然会执行下面的语句
            return ;
        }
        //解析token
        String userId;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            userId = claims.getSubject();
            if(StringUtil.isNullOrEmpty(userId)) {
                throw new RuntimeException("token解析失败");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //从redis中获取用户的信息
        LoginUser loginUser = redisCache.getCacheObject(userId);
        if(ObjectUtil.isEmpty(loginUser)) {
            throw new RuntimeException("Redis中没有用户信息");
        }
        //将数据存储到SecurityContextHolder
        //todo 设置对应的权限信息
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //放行
        filterChain.doFilter(request, response);
    }
}

进行测试,授权成功。

RDAB模型例子(基本通用,看进行二次开发)

1.创建五个数据库 用户表,角色表,权限表,用户角色关联表,角色权限关联表。

2.编写SQL语句查询用户的所有权限,并使用 mybatis-plus进行封装为一个函数进行调用。

SELECT
            DISTINCT m.`perms`
        FROM
            sys_user_role ur
                LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id`
                LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
                LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
        WHERE
            user_id = #{id}
          AND r.`status` = 0
          AND m.`status` = 0

3.在校验是进行调用,并返回对应的权限集合。

@Service
public class UserService implements UserDetailsService {

    @Autowired
    UserMapper userMapper;
    @Autowired
    MenuMapper menuMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<com.huang.springsecuritydemo.entity.User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("user_name", username);
        com.huang.springsecuritydemo.entity.User user = userMapper.selectOne(userQueryWrapper);
        if(ObjectUtil.isEmpty(user)) {
            throw new RuntimeException("用户不存在");
        }
        //todo 查询并设置对应的权限信息
        List<String> data = menuMapper.selectPermsByUserId(user.getId());

        return new LoginUser(user, data);
    }
}

修改接口所需要的权限。

@RestController
public class baseController {

    @RequestMapping("/hello")
    //拥有text倾向才能访问
    @PreAuthorize("hasAuthority('system:test:list')")
    public String hello() {
        return "hello!";
    }
}

进行测试,最终成功。

自定义失败处理

1.自定义授权异常处理器和校验异常处理器。

  • 校验异常处理器
//校验失败异常处理器
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        //创建统一的返回对象,设置到response中
        ResponseResult responseResult = new ResponseResult(HttpStatus.HTTP_UNAUTHORIZED, "校验失败!");
        String json = JSON.toJSONString(responseResult);
        //将统一的结果设置到Response中,本质就是将对应的数据设置到response中
        WebUtil.renderString(response, json);
    }
}
  • 授权异常处理器

//授权失败异常处理器
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        ResponseResult responseResult = new ResponseResult(HttpStatus.HTTP_UNAUTHORIZED, "授权失败!");
        String json = JSON.toJSONString(responseResult);
        WebUtil.renderString(response, json);
    }
}

对应的webUtil工具类

public class WebUtil {
    /**
     * 将字符串渲染到客户端
     *
     * @param response 渲染对象
     * @param string 待渲染的字符串
     * @return null
     */
    public static String renderString(HttpServletResponse response, String string) {
        try
        {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            //将结果json以流的形式写入response中
            response.getWriter().print(string);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }
}

2.将自定义的异常处理器进行配置

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    AccessDeniedHandler accessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //将AuthenticationManager配置到ioc中
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //配置权限规则,主要就睡要放行登录接口,不然登录接口都会被了解,以及其他不要的前后端分离的配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //由于是前后端分离项目,所以要关闭csrf
                .csrf().disable()
                //由于是前后端分离项目,没有session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //指定让spring security放行登录接口的规则
                .authorizeRequests()
                // 对于登录接口 anonymous表示允许匿名访问, permitAll就是 登录和没登录都可以访问
                .antMatchers("/user/login").anonymous() //匿名访问,未登录就可以访问
                // 除上面外的所有请求全部需要鉴权认证后访问
                .anyRequest().authenticated();
        //将过滤器添加到用户登录处理器之前
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        //设置自定义的异常处理器
        http.exceptionHandling()
                //校验异常处理器
                .authenticationEntryPoint(authenticationEntryPoint)
                //授权异常处理器
                .accessDeniedHandler(accessDeniedHandler);
    }
}

进行测试,异常显示成功。

允许跨域

1.开启springboot的允许跨域。

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    //重写spring提供的WebMvcConfigurer接口的addCorsMappings方法
    public void addCorsMappings(CorsRegistry registry) {
        //设置可以跨域的映射地址
        registry.addMapping("/**")
                // 设置可以跨域的源
                .allowedOriginPatterns("*")
                // 是否允许使用cookie
                .allowCredentials(true)
                // 设置允许的请求方式
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                // 设置允许的header属性
                .allowedHeaders("*")
                // 跨域允许时间
                .maxAge(3600);
    }
}

2.开启springsecurity的允许跨域。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    AccessDeniedHandler accessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //将AuthenticationManager配置到ioc中
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //配置权限规则,主要就睡要放行登录接口,不然登录接口都会被了解,以及其他不要的前后端分离的配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //由于是前后端分离项目,所以要关闭csrf
                .csrf().disable()
                //由于是前后端分离项目,没有session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //指定让spring security放行登录接口的规则
                .authorizeRequests()
                // 对于登录接口 anonymous表示允许匿名访问, permitAll就是 登录和没登录都可以访问
                .antMatchers("/user/login").anonymous() //匿名访问,未登录就可以访问
                // 除上面外的所有请求全部需要鉴权认证后访问
                .anyRequest().authenticated();
        //将过滤器添加到用户登录处理器之前
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        //设置自定义的异常处理器
        http.exceptionHandling()
                //校验异常处理器
                .authenticationEntryPoint(authenticationEntryPoint)
                //授权异常处理器
                .accessDeniedHandler(accessDeniedHandler);
        //允许跨域
        http.cors();
    }
}

最终设置完成。

自定义权限校验方法(比较灵活,可以自定义策略)

1.自定义校验类

@Component("itolen") //设置该类在ioc中的名称
public class ExpressionRoot {

    //判断权限是否存在
    public boolean hasAuthority(String authority) {
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        List<String> authorities2String = loginUser.getAuthorities2String();
        return authorities2String.contains(authority);
    }
}

2.在对应的接口上调用自定义方法。

@RestController
public class baseController {

    @RequestMapping("/hello")
    //拥有text倾向才能访问
    @PreAuthorize("@itolen.hasAuthority('system:test:list')")
    public String hello() {
        return "hello!";
    }
}

进行测试。

 其他处理器 

  • 认证成功处理器和认证失败处理器
//认证成功处理器实现类
@Component
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //认证成功后就会进行该方法
        System.out.println("认证成功!");
    }
}
//认证失败处理器实现类
@Component
public class AuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        //认证失败后执行该方法
        System.out.println("认证失败!");
    }
}

 将两个类进行配置。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    //配置权限规则,主要就睡要放行登录接口,不然登录接口都会被了解,以及其他不要的前后端分离的配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //设置校验成功处理器和校验失败处理器
        http.formLogin().successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler);
    }
}
  • 注销成功处理器
@Component
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //注销成功后执行的方法
        System.out.println("注销成功!");
    }
}

将该类进行配置。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

 
    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;

  
    //配置权限规则,主要就睡要放行登录接口,不然登录接口都会被了解,以及其他不要的前后端分离的配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        
        //设置注销成功处理器
        http.logout().logoutSuccessHandler(logoutSuccessHandler);
    }
}

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

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

相关文章

idea2023连接gitee远程仓库

目录 1.在gitee创建远程仓库 2.在Idea里配置git 3.初始化本地仓库 4. 提交推送至远程仓库 注意&#xff1a;提前下好git工具、idea2023&#xff0c;注册gitee账号&#xff0c;本文不介绍 1.在gitee创建远程仓库 创建好后&#xff0c;复制远程仓库地址 2.在Idea里配置git ​ …

分布式【Zookeeper】

1.1 ZooKeeper 是什么 ZooKeeper 是 Apache 的顶级项目。ZooKeeper 为分布式应用提供了高效且可靠的分布式协调服务&#xff0c;提供了诸如统一命名服务、配置管理和分布式锁等分布式的基础服务。在解决分布式数据一致性方面&#xff0c;ZooKeeper 并没有直接采用 Paxos 算法&…

视图与索引连表查询(内/外联)和子查询

目录 一、视图 1.1、概念&#xff1a; 1.2、场景&#xff1a; 1.3、用视图的意义 1.2、创建(增加)视图 1.3、修改视图 1.4、删除视图 1.5、查看视图 ​编辑 二、索引 2.1、概念 2.2、优缺点 优点&#xff1a; 缺点&#xff1a; 2.3、应用场景 2.4、会失效 2.5、…

项目框架构建之3:Nuget服务器的搭建

本文是“项目框架构建”系列之3&#xff0c;本文介绍一下Nuget服务器的搭建&#xff0c;这是一项简单的工作&#xff0c;您或许早已会了。 1.打开vs2022创建Asp.net Web应用程序 框架选择.net framework4.8&#xff0c;因为nuget服务器只支持.net framework。 2.选择空项目和保…

Docker nginx容器代理播放m3u8视频文件(HLS)

文章目录 Docker Nginx容器代理播放M3U8文件教程获取Nginx Docker镜像设置Nginx配置文件用 ffmpeg 将 MP4 文件转换成 m3u8 文件运行Docker容器测试M3U8流其他问题我用vlc都能播放http://192.168.121.50/forest4kTest.m3u8和http://192.168.121.50/forest4kTest.mp4&#xff0c…

如何安装和使用夜神模拟器连接Android Studio

目录 简介 一、安装 二、使用 三、更多资源 简介 夜神模拟器是一款在Windows平台上运行的Android模拟器软件。它能够模拟Android操作系统环境&#xff0c;让用户在电脑上轻松体验Android应用程序。夜神模拟器的功能强大&#xff0c;可以满足各种需求&#xff0c;无论是娱乐…

QT----Visual stdio翻金币案例,附源码

历经一个月&#xff0c;各种事情磕磕绊绊&#xff0c;终于结束了&#xff0c;自己还是太菜了 案例的文档写的教程已经很详细&#xff0c;这边主要是记录一些问题 github代码 gitee代码 1、图片无法加载 一开始加载首页图片和标题出不来&#xff0c;结果是paintEvent重写的字打…

【LMM 014】NExT-GPT:能够输入和生成任意模态的多模态大模型

论文标题&#xff1a;NExT-GPT:Any-to-Any Multimodal Large Language Model 论文作者&#xff1a;Shengqiong Wu, Hao Fei*, Leigang Qu, Wei Ji, Tat-Seng Chua 作者单位&#xff1a; NExT Lab, National University of Singapore 论文原文&#xff1a;https://arxiv.org/abs…

线性代数 --- 为什么LU分解中L矩阵的行列式一定等于正负1?

以下是关于下三角矩阵L的行列式一定等于-1的一些说明 笔者的一些话(写在最前面)&#xff1a; 这是一篇小文&#xff0c;是我写的关于求解矩阵行列式的一篇文章中的一部分。之所以把这一段专门提溜出来&#xff0c;是因为这一段相对于原文是可以完全独立的&#xff0c;也是因为我…

RocketMQ 投递消息方式以及消息体结构分析:Message、MessageQueueSelector

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&…

条件竞争之文件上传

一、条件竞争介绍 条件竞争,在程序员日常的Web应用开发中&#xff0c;通常不如其他漏洞受到的关注度高。因为普遍的共识是&#xff0c;条件竞争是不可靠的&#xff0c;大多数时候只能靠代码审计来识别发现&#xff0c;而依赖现有的工具或技术很难在黑盒灰盒中识别并进行攻击。…

精进单元测试技能 —— Pytest断言的艺术!

本篇文章主要是阐述Pytest在断言方面的应用。让大家能够了解和掌握Pytest针对断言设计了多种功能以适应在不同测试场景上使用。 了解断言的基础 在Pytest中&#xff0c;断言是通过 assert 语句来实现的。简单的断言通常用于验证预期值和实际值是否相等&#xff0c;例如&#…

小游戏实战丨基于PyGame的消消乐小游戏

文章目录 写在前面PyGame消消乐注意事项系列文章写在后面 写在前面 本期内容&#xff1a;基于pygame实现喜羊羊与灰太狼版消消乐小游戏 下载地址&#xff1a;https://download.csdn.net/download/m0_68111267/88700193 实验环境 python3.11及以上pycharmpygame 安装pygame…

ENVI 各版本安装指南

ENVI下载链接 https://pan.baidu.com/s/1APpjHHSsrXMaCcJUQGmFBA?pwd0531 1.鼠标右击【ENVI 5.6(64bit&#xff09;】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;选择【解压到 ENVI 5.6(64bit&#xff09;】。 2.打开解压后的文件夹&#xff0c…

一些想法:关于行人检测与重识别OIMLoss

本文主要是介绍我们录用于 ECCV18 的一个工作&#xff1a;Person Search via A Mask-guided Two-stream CNN Model. 这篇文章着眼于 Person Search 这个任务&#xff0c;即同时考虑行人检测&#xff08;Pedestrian Detection&#xff09;与行人重识别&#xff08;Person Re-ide…

2024PMP考试新考纲-【人员领域】近期典型真题和超详细解析(5)

今天华研荟继续为您分享PMP新考纲下的【人员People领域】近年真题&#xff0c;帮助大家举一反三&#xff0c;一次性通过2024年的PMP考试。 2024年PMP考试新考纲-【人员领域】真题解析21 题&#xff1a;项目经理正在为一个项目工作。该项目由于人员流动&#xff0c;相关方登记册…

Matlab二维绘图

低级绘图命令line 有什么点就点哪里&#xff0c;然后连起来&#xff0c;没什么细节&#xff0c;不光滑&#xff0c;所以基本不会用到。 x0:0.2*pi:2*pi; ysin(x); line(x,y);%画一条sin函数线 line([-5,5],[2,2]);%画一条水平线 line([5,5],[0,2]);%画一条竖线 高级绘图命令…

MySQL之视图外连接、内连接和子查询的使用

一、视图 1.1 含义 虚拟表&#xff0c;和普通表一样使用 1.2 操作 创建视图 create view 视图名 as 修改视图 方式一&#xff1a; create or replace view 视图名 as 【查看视图相关字段】 方式二&#xff1a; alter view 视图名 as 【查看的SQL语句】 查看视图 方式一&…

BUUCTF--actf_2019_babyheap1

这题看名字就知道是堆题&#xff0c;先看保护&#xff1a; 保护除了PIE全开&#xff0c;黑盒测试&#xff1a; 题目提供增删查&#xff0c;没有改。看看IDA中代码逻辑&#xff1a; 逻辑跟我前面做的一题极为相似&#xff0c;就不过多分析。 这是free&#xff1a; 因为题目不能…

基于SSM的驾校考试预约管理设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…