springsecurity的学习(四):实现授权

news2024/9/23 3:13:36

简介

springsecurity的授权,自定义授权失败的处理,跨域的处理和自定义权限校验方法的介绍

授权

权限系统作用

在后台进行用户权限的判断,判断当前用户是否有相应的权限,必须具有所需的权限才能进行相应的操作,以此达到不同的用户可以使用不同的功能。

流程

会使用springsecurity默认的FilterSecurityInterceptor来进行权限校验,会从SecurityContextHolder获取其中的Authentication,从Authentication中获取权限的信息,判断当前用户是否拥有当前资源所需的权限。

实现方式

springsecurity提供了基于注解的权限控制方案,使用注解去指定访问对应的资源所需的权限。
需要在配置类中添加注解@EnableGlobalMethodSecurity 注解开启相关的配置
开启后,即可在controller的接口上添加使用springsecurity的权限相关的注解。如
@PreAuthorize("hasAuthority('权限字符串')"):可以判断当前访问接口的用户是否有这个权限

数据库查询权限

rabc权限模型

基于角色的权限控制。

创建表

需要5张表
在这里插入图片描述
创建语句:

CREATE TABLE `sys_menu` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `menu_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '菜单名',
  `path` varchar(200) DEFAULT NULL COMMENT '路由地址',
  `component` varchar(255) DEFAULT NULL COMMENT '组件路径',
  `visible` char(1) DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)',
  `status` char(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)',
  `perms` varchar(100) DEFAULT NULL COMMENT '权限标识',
  `icon` varchar(100) DEFAULT '#' COMMENT '菜单图标',
  `create_by` bigint DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_by` bigint DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `del_flag` int DEFAULT '0' COMMENT '是否删除(0未删除 1已删除)',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='权限表';

#################################
CREATE TABLE `sys_role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(128) DEFAULT NULL,
  `role_key` varchar(100) DEFAULT NULL COMMENT '角色权限字符串',
  `status` char(1) DEFAULT '0' COMMENT '角色状态(0正常 1停用)',
  `del_flag` int DEFAULT '0' COMMENT 'del_flag',
  `create_by` bigint DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_by` bigint DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='角色表';
#################################
CREATE TABLE `sys_role_menu` (
  `role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `menu_id` bigint NOT NULL DEFAULT '0' COMMENT '菜单id',
  PRIMARY KEY (`role_id`,`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
#################################
CREATE TABLE `sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '用户名',
  `nick_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '昵称',
  `password` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '密码',
  `status` char(1) DEFAULT '0' COMMENT '账号状态(0正常 1停用)',
  `email` varchar(64) DEFAULT NULL COMMENT '邮箱',
  `phonenumber` varchar(32) DEFAULT NULL COMMENT '手机号',
  `sex` char(1) DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',
  `avatar` varchar(128) DEFAULT NULL COMMENT '头像',
  `user_type` char(1) NOT NULL DEFAULT '1' COMMENT '用户类型(0管理员,1普通用户)',
  `create_by` bigint DEFAULT NULL COMMENT '创建人的用户id',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` bigint DEFAULT NULL COMMENT '更新人',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `del_flag` int DEFAULT '0' COMMENT '删除标志(0代表未删除,1代表已删除)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';
#################################
CREATE TABLE `sys_user_role` (
  `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `role_id` bigint NOT NULL DEFAULT '0' COMMENT '角色id',
  PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
根据userid查询权限
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

在这里插入图片描述

Menu 类
package com.springSecurityTest.common;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@TableName(value="sys_menu") //指定表名,避免等下mybatisplus的影响
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
//Serializable是官方提供的,作用是将对象转化为字节序列
public class Menu implements Serializable {
    private static final long serialVersionUID = -54979041104113736L;

    @TableId
    private Long id;
    /**
     * 菜单名
     */
    private String menuName;
    /**
     * 路由地址
     */
    private String path;
    /**
     * 组件路径
     */
    private String component;
    /**
     * 菜单状态(0显示 1隐藏)
     */
    private String visible;
    /**
     * 菜单状态(0正常 1停用)
     */
    private String status;
    /**
     * 权限标识
     */
    private String perms;
    /**
     * 菜单图标
     */
    private String icon;

    private Long createBy;

    private Date createTime;

    private Long updateBy;

    private Date updateTime;
    /**
     * 是否删除(0未删除 1已删除)
     */
    private Integer delFlag;
    /**
     * 备注
     */
    private String remark;
}

MenuMapper类
package com.springSecurityTest.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.springSecurityTest.common.Menu;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;
@Mapper
public interface MenuMapper extends BaseMapper<Menu> {
    List<String> selectMemusByUserId(Long userId);
}

menu.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.springSecurityTest.mapper.MenuMapper">
    <select id="selectMemusByUserId" resultType="java.lang.String">
        select distinct sys_menu.perms from sys_user_role
left join sys_role
on sys_user_role.role_id = sys_role.id
left join sys_role_menu
on sys_user_role.role_id = sys_role_menu.role_id
left join sys_menu
on sys_menu.id = sys_role_menu.menu_id
where user_id = #{userid}
and sys_role.`status` = 0

    </select>
</mapper>

springsecurity授权

UserDetailsServiceImpl类

把权限信息放入到loginuser中

package com.springSecurityTest.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.springSecurityTest.mapper.MenuMapper;
import com.springSecurityTest.mapper.UserMapper;
import com.springSecurityTest.common.LoginUser;
import com.springSecurityTest.common.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Resource
    UserMapper userMapper;
    @Resource
    private MenuMapper menuMapper;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getUserName,s);
        User user = userMapper.selectOne(lambdaQueryWrapper);
        if (Objects.isNull(user)){
            throw new RuntimeException("用户名或密码错误");
        }
        //查询权限
        List<String> list = menuMapper.selectMemusByUserId(user.getId());
        return new LoginUser(user,list);
    }
}
LoginUser 类

添加权限的属性,重写getAuthorities方法。把permissions中的权限信息封装成simpleGrantauthority对象

package com.springSecurityTest.common;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails {
    private User user;
    private List<String> permissions;
    public LoginUser(User user,List<String> permissions){
        this.user = user;
        this.permissions = permissions;
    }
    @JSONField(serialize = false)
    private List<GrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if(authorities!= null){
            return authorities;
        }
        authorities = new ArrayList<>();
        for(String permission:permissions){
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permission);
            authorities.add(simpleGrantedAuthority);
        }
        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;
    }
}

JwtAuthenticationTokenFilter 类

给usernamePasswordAuthenticationToken 对象添加权限

package com.springSecurityTest.filter;

import com.springSecurityTest.common.LoginUser;
import com.springSecurityTest.utils.JwtUtil;
import com.springSecurityTest.utils.RedisCache;
import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import sun.plugin.liveconnect.SecurityContextHelper;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Resource
    private RedisCache redisCache;
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String token = httpServletRequest.getHeader("token");
        if (!StringUtils.hasText(token)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }
        String userId;
        try {
            System.out.println(JwtUtil.parseJWT(token));
            Claims claims = JwtUtil.parseJWT(token);
            userId = claims.getSubject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("token非法");
        }
        String redisKey = "token:" + userId;
        LoginUser loginUser = redisCache.getCacheObject(redisKey);
        if(Objects.isNull(loginUser)){
            throw new RuntimeException("用户未登录");

        }
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }

}

controller类
package com.springSecurityTest.controller;

import com.springSecurityTest.common.User;
import com.springSecurityTest.mapper.UserMapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

@RestController
public class dark {
@RequestMapping("/dark")
@PreAuthorize("@hasAuthority('system:dept:list')")
    public String dark(){
    return "it's too dark!";
}

}

自定义失败

在springsecurity中,如果在认证或者授权的过程中出现了异常,会被ExceptionTranslationFilter捕获,然后调用如下对象的方法处理异常:

  • AuthenticationEntryPoint对象的方法会对认证过程中出现的异常进行处理
  • AccessDeniedHandler对象的方法会对授权过程中出现的异常进行处理。

要自定义异常处理,只需要自定义AuthenticationEntryPoint和AccessDeniedHandler,然后配置给springsecurity。

AccessDeniedHandlerImpl类
package com.springSecurityTest.handler;

import com.alibaba.fastjson.JSON;
import com.springSecurityTest.common.ResponseResult;
import com.springSecurityTest.utils.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Service;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Service
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "权限不足");
        String json = JSON.toJSONString(result);
        WebUtils.renderString(httpServletResponse,json);
    }
}
AuthenticationEntryPointImpl类
package com.springSecurityTest.handler;

import com.alibaba.fastjson.JSON;
import com.springSecurityTest.common.ResponseResult;
import com.springSecurityTest.utils.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Service;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Service
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        ResponseResult result = new ResponseResult(HttpStatus.UNAUTHORIZED.value(), "用户认证失败请重新登录");
        String json = JSON.toJSONString(result);
        WebUtils.renderString(httpServletResponse,json);
    }
}
WebUtils

上面两个类中用到的工具类

package com.springSecurityTest.utils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class WebUtils {
    /**
     * 将字符串渲染到客户端
     *
     * @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");
            response.getWriter().print(string);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }
}
SecurityConfig

添加两个异常处理器

package com.springSecurityTest.config;

import com.springSecurityTest.filter.JwtAuthenticationTokenFilter;
import com.springSecurityTest.handler.AuthenticationEntryPointImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Resource
    private AuthenticationEntryPointImpl authenticationEntryPoint;
    @Resource
    private AccessDeniedHandler accessDeniedHandler;
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
//testgit
    @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()
                .antMatchers("/dark").hasAuthority("system:test:list")
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();
        http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);
        //配置异常处理器
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler);
http.cors();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

跨域问题

同源策略:协议,域名,端口号要一致
在应用中启用了Spring Security,它默认会对所有的请求进行拦截和验证。这意味着,即使Spring Boot配置允许了CORS,Spring Security的默认配置也可能阻止跨域请求,因为它会检查每一个请求是否带有有效的认证信息。
为了使Spring Security与CORS协同工作,通常需要在Spring Security的配置中显式地允许CORS请求。

springboot配置
package com.springSecurityTest.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@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);
    }
}
springsecurity配置

在springsecurity的配置类SecurityConfig中,重写configure方法,加上http.cors();

自定义权限校验方法

package com.springSecurityTest.expression;

import com.springSecurityTest.common.LoginUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.util.List;

@Component("MyExpressionRoot ")
public class MyExpressionRoot {
    public boolean hasAuthority(String authority){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser  = (LoginUser) authentication.getPrincipal();
        List<String> permissions = loginUser.getPermissions();
        return permissions.contains(authority);
    }
}
使用自定义权限校验方法
package com.springSecurityTest.controller;

import com.springSecurityTest.common.User;
import com.springSecurityTest.mapper.UserMapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

@RestController
public class dark {
    @Resource
    UserMapper userMapper;
@RequestMapping("/dark")
@PreAuthorize("@MyExpressionRoot .hasAuthority('system:dept:list')")
    public String dark(){
    return "it's too dark!";
}
@GetMapping("/getUser")
    public List<User> usertest(){
    List<User> users = userMapper.selectList(null);
    System.out.println(users);
    return users;
}
}

CSRF

跨站请求伪造,是web常见攻击之一,依靠的是cookie中携带的认证信息,使用token可以不用担心csrf攻击,因为token不存储在cookie中,而且前端把token设置到请求头中访问网站资源。

扩展

如果登录页面还有验证码,那还可以在UsernamePasswordAuthenticationFilter之前再写一个验证码的过滤器,组成过滤器链。

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

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

相关文章

高通分享:glTF 2.0扩展MPEG、3GPP在AR/VR 3D场景的沉浸式体验

日前&#xff0c;高通技术标准高级总监托马斯斯托克哈默尔&#xff08;Thomas Stockhammer&#xff09;和高通技术标准总监伊梅德布亚齐兹&#xff08;Imed Bouazizi&#xff09;撰文分享了ISO和Khronos之间是如何紧密合作&#xff0c;并最终开发出MPEG-I Scene Description IS…

基于LangChain的大模型学习手册(入门级)

前言 时间轴来到2024年的下半年&#xff0c;“大模型”这个术语&#xff0c;从几乎是ChatGPT的代名词&#xff0c;转变为AI领域一个划时代产品的广泛词汇。从单一到广泛&#xff0c;代表大模型这个世界级产品&#xff0c;正在走向枝繁叶茂的生命阶段。截止现在&#xff0c;目前…

【算法分析与设计】期末复习-小题100道

目录 0、基础知识点 一、单选题 二、多选题 三、判断题 0、基础知识点 &#xff08;1&#xff09;常见时间复杂度与公式&#xff1a; 汉诺塔&#xff1a;T(n)O(2^n) 全排列&#xff1a;T(n)O(n!) 整数划分&#xff1a; 正整数n的划分&#xff1a;p(n)q(n,n) 分治&#…

推荐系统三十六式学习笔记:工程篇.效果保证31|推荐系统的测试方法及常用指标介绍

目录 为什么要关注指标推荐系统的测试方法1.业务规则扫描2.离线模拟测试3.在线对比测试4.用户访谈 常用指标1.系统有多好&#xff1f; 假设你已经有了自己的推荐系统&#xff0c;这个系统已经上线。 为什么要关注指标 面对推荐系统这样一个有诸多复杂因素联动起作用的系统&am…

C++入门:类和对象(入门篇)

目录 前言 类的定义 1.类定义格式 2.从结构体到类的跨越 3.访问限定符 4.类域 5.类的实例化 类的默认成员函数 1.默认成员函数的定义和学习方向 2.构造函数 3.析构函数 4.拷贝构造函数 5.重载运算符 总结 疑难解答 1.this指针的用法 2.为什么拷贝构造函数的第一个参数必须…

LeetCode 热题 HOT 100 (024/100)【宇宙最简单版】

【哈希表】No. 0128 最长连续序列【中等】&#x1f449;力扣对应题目指路 希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&am…

WPF中RenderTransform,LayoutTransform区别

RenderTransform RenderTransform 是在渲染阶段应用的变换。它不会影响控件的布局&#xff0c;只会影响控件的外观。常用于动画和视觉效果。 • 应用时机&#xff1a;在控件已经完成布局之后。 • 影响范围&#xff1a;仅影响控件的外观&#xff0c;不影响布局。 • 常见用途&…

汇川技术|PLC应用逻辑编程技巧(2)

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 昨天看这块儿内容看到快十二点了&#xff0c;还没看完&#xff0c;今天接着看。 以下为学习笔记。 01 PLC程序实现状态关系 该思路编写程序的要点如下&#xff1a; ①&#xff1a;采用PLC的一个整数型变量作为状态位…

路透社中东门户媒体ZAWYA:自带流量为品牌出海赋能

路透社中东门户媒体ZAWYA:自带流量为品牌出海赋能 随着全球化的不断推进&#xff0c;越来越多的企业开始将目光投向海外市场&#xff0c;寻求更广阔的发展空间。然而&#xff0c;在激烈的市场竞争中&#xff0c;如何让自己的品牌脱颖而出成为一个亟待解决的问题。在这个背景下…

Windows 11系统SQL Server 2016 数据库安装 最新2024教程和使用

文章目录 目录 文章目录 安装流程 小结 概要安装流程技术细节小结 概要 文件可以关注作者公众号《全栈鍾猿》&#xff0c;发您 安装流程 双击运行 在资源管理器页面如图所示 点击全选-->取消勾选如图所示的3个---》点击下一步 点击下一步 安装完成&#xff0c;如图所示 &a…

USB-HUB电路设计

USB-HUB电路设计 USB-HUB电路是笔者任职电子工程师做的第一块板子,功能为USB3.0的集线器,主芯片采用RTS5411,最多能同时工作四个USB3.0设备。 由于信号有TX.RX,我们很容易将发送端和接收端搞错,毕竟从芯片出来,要经过很多的路径,如USB端子,线材,再到芯片。则芯片出来…

内网渗透—横向移动RDPWinRMWinRSSPN扫描Kerberos攻击

前言 今天仍是横向移动的内容&#xff0c;有些实验能成功&#xff0c;有些实验则各种奇怪的问题导致失败&#xff0c;这都是很常见的。就连小迪在视频中也经常翻车&#xff0c;我们只需要知道原理&#xff0c;以及如何去实现这个攻击行为即可。没必要强求所有的实验都要百分百…

Python3网络爬虫开发实战(8)验证码的识别

文章目录 一、 图片增强&#xff1a;OpenCV1. OpenCV 基础使用2. 滑动验证码缺口识别 二、图片验证码和滑块验证码1. tesserocr2. ddddocr3. 深度学习识别4. 超级鹰打码平台 三、手机验证码的自动化处理 目前&#xff0c;许多网站采取各种各样的措施来反爬虫&#xff0c;其中一…

嵌入式面经篇四——内存管理

文章目录 前言一、内存管理&编程题1、由gcc编译的C语言程序占用的内存分为哪几个部分&#xff1f;2、大小端3、全局变量和局部变量的区别&#xff1f;4、以下程序中&#xff0c;主函数能否成功申请到内存空间&#xff1f;5、请问运行下面的 Test() 函数会有什么样的后果&am…

Anaconda安装jupyter notebook、jupyterlab及体验

Anaconda安装jupyter 1.前言2.创建虚拟环境3.激活虚拟环境4.安装jupyter notebook5.启动6.快捷方式7.jupyterlab插件安装8.报错的处理9.总结1.前言 手贱,不小心将Anaconda自带得jupyter给卸载了,没法子了查了好多资料都比较麻烦,所以自己摸索着重新安装,记录一下心得。 说…

87.SAP Transaction SM31 and SM30

目录 1.SM30是什么 2.SM30维护表的前提条件&#xff1a; 3.用途 维护表数据 查看SAP表的配置点 4.SM31和SM30的区别 1.SM30是什么 Transaction code SM30 can be used to display and update table data. The input field on the first screen of SM30 is long enough t…

企业通用报表平台代码审计

1 第三方组件漏洞审计 本项目是基于Maven构建的。对于Maven项目,我们首先从 pom.xml 文件开始审计引入的第三方组件是 否存在漏洞版本,然后进一步验证该组件是否存在漏洞点。 本项目引入的组件以及组件版本整理如下。 组件名称组件版本SpringBoot2.2.4.RELEASEFastjson1.2…

CDP问卷填报手册指南

CDP认证的作用是向消费者和利益相关者提供一个可信的证明&#xff0c;证明该组织正在采取行动&#xff0c;以减少其对气候变化的影响并提高可持续发展。CDP认证是一个独立的评估过程&#xff0c;通过评估组织在应对气候变化和可持续发展方面的表现和策略&#xff0c;评估其对气…

免费下载专利

给大家提供一个可以免费下载专利的地方 链接&#xff1a;https://www.drugfuture.com/cnpat/cn_patent.asp

玩转生产环境全链路压测

一、什么是生产环境全链路压测 生产环境全链路压力测试&#xff08;Production Environment Full-Link Stress Testing&#xff09;是一种针对线上系统进行的综合性性能测试方法。这个过程涉及模拟实际用户行为&#xff0c;从用户界面到后端数据库的整个应用链路上施加预定的高…