SpringSecurity中授权时fastjson序列化问题

news2025/1/18 8:16:08

最近在复习Spring Security,复习的鉴权的时候出现问题。26.封装权限信息_哔哩哔哩_bilibili

如果是从B站中看到,直接说问题可能出现的原因:可能是private List<String> authorities;中的权限信息命名不规范,去掉get,A变小写。如果要细看原因请往下看。

 下图是代码中aaaaa是我测试的权限信息集合,故意命名写的不规范,才知道原因。

问题场景:使用Spring Security模拟授权的时候,手动存入几个权限信息,然后正确的访问时出现了问题。

重要的事情放前面说:如果不配置权限集合如果不加@JSONField(serialize = false)注解,解析的时候会报错。

 UserDetailServiceImpl.java   UserDetailsService实现类

@Service
public class UserDetailServiceImpl implements UserDetailsService {


    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询用户信息

        LambdaQueryWrapper<User> queryChainWrap = new LambdaQueryWrapper<>();
        queryChainWrap.eq(!Objects.isNull(username), User::getUserName, username);
        User user = userMapper.selectOne(queryChainWrap);
        // 如果没有查询到用户就抛出异常
        if (Objects.isNull(user)) {
            throw new RuntimeException("用户名或密码错误");
        }

        //TODO 查询对应的权限信息
        ArrayList<String> list = new ArrayList<>(Arrays.asList("test", "admin"));

        // 把数据封装成UserDetails返回
        return new LoginUser(user, list);
//        return new LoginUser(user);
    }
}

LoginServiceImpl.java   自定义登录接口实现类

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private AuthenticationManager authenticationManager;


    @Autowired
    private RedisCache redisCache;

    @Override
    public ResponseResult login(User user) throws Exception {
        //AuthenticationManager authenticate进行用户认证
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
//        authenticate.getPrincipal()
        // 如果认证没有通过,给出对应的提示
        if (Objects.isNull(authenticate)) {
            throw new RuntimeException("登录失败");
        }
        // 如果认证通过了,使用userid生成jwt jwt存入ResponseResult返回
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(userId);

        Map<String, String> map = new HashMap<>();
        map.put("token", jwt);
        // 把完整的用户信息存入redis userid作为key
        redisCache.setCacheObject(SystemConstants.LOGIN_TOKEN_PREFIX + userId, loginUser);
        return new ResponseResult(200, "登录成功", map);
    }

    @Override
    public ResponseResult logout() {
        // 获取SecurityContextHolder中的用户id
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();

        Long userId = loginUser.getUser().getId();
        // 删除redis中的值
        redisCache.deleteObject(SystemConstants.LOGIN_TOKEN_PREFIX + userId);
        return new ResponseResult(200, "注销成功");
    }
}

JwtAuthenticationTokenFilter.java   自定义过滤器

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {


    @Autowired
    private RedisCache redisCache;

    @Autowired
    private AuthenticationManager authenticationManager;

    @SneakyThrows(value = {RuntimeException.class, ArrayIndexOutOfBoundsException.class, Exception.class})
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取token
        String token = request.getHeader("token");
        // token为内容为空或者不以login:开头直接放行
        if (!StringUtils.hasText(token)) {
            // 放行
            filterChain.doFilter(request, response);
            return;
        }
        // 解析token获取其中的userId
        String userId;
        try {
            userId = JwtUtil.parseJWT(token).getSubject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("token非法");
        }
        // 从redis中获取用户信息
        String redisKey = SystemConstants.LOGIN_TOKEN_PREFIX + userId;
        LoginUser loginUser = redisCache.getCacheObject(redisKey);
        if (Objects.isNull(loginUser)) {
            throw new RuntimeException("用户未登录");
        }
        // 存入SecurityContextHolder
        // TODO 获取权限信息封装到Authentication
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        // 这里不知道为什么加这句,导致进行验证,走了后续过滤器,但是我发送的测试请求是带token的get请求,
        // 但是我的数据是从redis中获取,密码是加密后的,导致二次加密后,密码不一致
//        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);

        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        // 放行
        filterChain.doFilter(request, response);
    }
}

 待测试的Controller

 LoginUser.java   UserDetails实现类

@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {

    private User user;

    private List<String> permission;


    @JSONField(serialize = false)
//    @JsonIgnore
//    @JsonIgnoreProperties
    private List<GrantedAuthority> aaaaa;

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

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

    public LoginUser(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // 把permission中String类型的去啊年信息封装成SimpleGrantedAuthority对象
        if (Objects.isNull(aaaaa)) {
            synchronized (this) {
                if (Objects.isNull(aaaaa)) {
                    aaaaa
                            = permission.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
                }
            }
        }
        return aaaaa;
//        ArrayList<GrantedAuthority> list = new ArrayList<>();
//        list.add(new SimpleGrantedAuthority("Powerveil"));
//        return list;
    }

    @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;
    }
}

 直接看结果

 没有任何输出

看一下idea的console

2023-06-18 18:46:34.557 ERROR 21216 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

com.alibaba.fastjson.JSONException: autoType is not support. org.springframework.security.core.authority.SimpleGrantedAuthority
	at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:830) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:596) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:226) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:222) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:716) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.serializer.CollectionCodec.deserialze(CollectionCodec.java:120) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:78) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:911) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:656) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:226) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:222) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:357) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1325) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.deserializer.JavaObjectDeserializer.deserialze(JavaObjectDeserializer.java:45) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:630) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:354) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:258) ~[fastjson-1.2.33.jar:na]
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:471) ~[fastjson-1.2.33.jar:na]
	at com.powerveil.utils.FastJsonRedisSerializer.deserialize(FastJsonRedisSerializer.java:56) ~[classes/:na]
	at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:335) ~[spring-data-redis-2.5.1.jar:2.5.1]
	at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:61) ~[spring-data-redis-2.5.1.jar:2.5.1]
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:222) ~[spring-data-redis-2.5.1.jar:2.5.1]
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:189) ~[spring-data-redis-2.5.1.jar:2.5.1]
	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96) ~[spring-data-redis-2.5.1.jar:2.5.1]
	at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:53) ~[spring-data-redis-2.5.1.jar:2.5.1]
	at com.powerveil.utils.RedisCache.getCacheObject(RedisCache.java:78) ~[classes/:na]
	at com.powerveil.filter.JwtAuthenticationTokenFilter.doFilterInternal(JwtAuthenticationTokenFilter.java:61) ~[classes/:na]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) ~[spring-security-web-5.5.0.jar:5.5.0]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.3.7.jar:5.3.7]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.3.7.jar:5.3.7]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.7.jar:5.3.7]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.7.jar:5.3.7]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.7.jar:5.3.7]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_321]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_321]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at java.lang.Thread.run(Thread.java:750) [na:1.8.0_321]

错误日志多不要害怕

 autoType is not support   不支持自动类型

看下面的错误

RedisCache.java是一个自定义的Redis工具类

原来是redis取出数据出现问题,看一下redis中存储的数据。

{
  "@type": "com.powerveil.domain.LoginUser",
  "accountNonExpired": true,
  "accountNonLocked": true,
  "authorities": [
    {
      "@type": "org.springframework.security.core.authority.SimpleGrantedAuthority",
      "authority": "test"
    },
    {
      "@type": "org.springframework.security.core.authority.SimpleGrantedAuthority",
      "authority": "admin"
    }
  ],
  "credentialsNonExpired": true,
  "enabled": true,
  "password": "$2a$10$4MzpJkpyo2gywu5B/b6zEO3gfyYqcEN.6PG5wEpcb9nASzPddq7fu",
  "permission": [
    "test",
    "admin"
  ],
  "user": {
    "delFlag": 0,
    "email": "zcuishuai@yeah.net",
    "id": 1,
    "nickName": "Powerveil",
    "password": "$2a$10$4MzpJkpyo2gywu5B/b6zEO3gfyYqcEN.6PG5wEpcb9nASzPddq7fu",
    "phonenumber": "123456789",
    "sex": "0",
    "status": "0",
    "userName": "Powerveil",
    "userType": "1"
  },
  "username": "Powerveil"
}

看到redis数据,怎么这么多字段

我自定义了只有三个字段

user

permission

aaaaa

 user,permisssion都看到出来,但是我的aaaaa怎么没有了,正常,因为使用了@JSONField(serialize = false)注解,不会写入redis中。

postman返回的结果

上面是我第一次遇到的情况,以下是其他测试情况,看完你就知道原因了。

2.将getAuthorities方法的返回值设为null其他不变。

redis中的结果 

发现authorities消失了

数据可以正常解析

但是结果403,但是也正常,因为鉴权的方法的返回值是null。

3.添加authorities字段

 方法正常重写

查看redis数据

发现authorities字段生成

redis数据解析错误

 postman返回的结果

4.给authorities字段加上@JSONField(serialize = false)注解

查看redis数据,发现authorities字段消失

可以获取正常解析redis数据到loginUser

 可以输出结果

我推测是Spring Security会判断authorities字段是否存在如果getAuthorities方法返回值为null或者该字段加上@JSONField(serialize = false),则不会存入redis中。如下图

其他情况会存入redis中。如下图

但是我们最后要做的是鉴权的时候,方法的返回值不能是null呀。所以只有给authorities字段加上@JSONField(serialize = false)注解才能解决问题。现在突然醒悟了,那我们还要aaaaa这个字段干什么,直接加上authorities字段再给它配个注解,方法里面都写authorities不就解决问题了?是的,确实是这样,因为Spring Security检查的是authorities字段加上@JSONField(serialize = false)注解才会判断要不要写入redis。这个aaaaa这个字段确实多余。

看到这里,大家可能知道问题的原因了,是因为没有定义authorities字段(不能写错!!!)。

查看效果

redis中的数据

 redis解析正常

postman返回数据正常

思考:LoginUser类中字段只写了三个,而且一个加入注解忽略写入redis,为什么还有这么多其他字段?

看一下LoginUser的方法

像不像方法与字段像对应

这样就清晰了,getAuthorities方法应该也可以对应一个字段authorites,如果返回值不为null那个就会有生成字段。存入redis的时候发现这个生成的authorites字段,因为是生成的,没有配置@JSONField(serialize = false)注解,所以会直接存入redis。取出的时候发现没有这个字段,就会报错,就算有这个字段,也解析不出来(重要的事情在文章场景描述的地方)。

这个提醒了我们什么?要规范命名(手动狗头)

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

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

相关文章

Python基础篇(十一):装饰器

装饰器 前言1. 装饰器的定义2. 装饰器的应用3. 装饰器的语法4. func(*args, **kwargs) 前言 装饰器是Python中一种强大的函数或类修饰机制&#xff0c;用于在不修改原始函数或类代码的情况下&#xff0c;对其进行功能扩展或修改。装饰器基于函数式编程的概念&#xff0c;通过将…

【计算机网络详解】——知识点复习(期末不挂科版)

配套视频&#xff1a;湖科大教书匠 配套教材&#xff1a;计算机网络&#xff08;第8版&#xff09; 谢希仁 根据学习通要求“掌握”的部分总结的&#xff0c;有亿点点长 &#xff0c;时间来不及就掌握画 ☆ 的就好啦 目录 &#x1f552; 1. 概述&#x1f558; 1.1 三种数据交换…

[英语单词] gated; cycle gated

gate *[geit] n. 门, 牌楼, 大门, 通道, 闸 vt. 装门于 门的原始作用就显示出来了&#xff0c;就是为了在主人自由进出的同时&#xff0c;保护私有财产的安全。 如果加了-ed&#xff0c;就代表使用了动词性&#xff0c;相当于给谁谁加了一个门&#xff0c;限制其行为的自由度。…

SpringBoot 如何使用 ACL 进行访问控制

SpringBoot 如何使用 ACL 进行访问控制 在现代 Web 开发中&#xff0c;访问控制是一个非常重要的问题。访问控制可以帮助我们保护敏感信息、防止恶意攻击、维护系统安全等。Spring Security 是一个非常流行的安全框架&#xff0c;它提供了一系列的认证和授权功能&#xff0c;可…

【网络1】协议及相关命令

文章目录 1.局域网&#xff1a;CSMA/CD2.互联网&#xff1a;ARP&#xff0c;DHCP&#xff0c;NAT3.TCP协议&#xff1a;telnet&#xff0c;tcpdump&#xff0c;syn/accept队列4.HTTPS协议&#xff1a;摘要&#xff08;sha、md5、crc&#xff09;。win对文件MD5校验&#xff1a;…

【博弈论笔记】第三章 完全且完美信息动态博弈

文章目录 第三章 完全且完美信息动态博弈3.1 动态博弈的表示法和特点3.2 策略的可信性和纳什均衡的不稳定问题3.2.1 相机选择和策略的可信性问题3.2.2 纳什均衡的不稳定问题3.2.3 逆推归纳法 3.3 子博弈和子博弈完美纳什均衡3.3.1 子博弈3.3.2 子博弈完美纳什均衡 3.4 四个经典…

规则引擎--函数式编程和and/or操作符的设计

目录 Java函数编程的一些基础知识BiFunctionBinaryOperatorstream reduce And, Or操作符and 逻辑 的 Combiner 如下:or 逻辑 的 Combiner 如下:and, or的执行 接上一篇博文&#xff1a;规则引擎–规则逻辑形如“1 & (2 | 3)“的抽象&#xff0c; 重点分析一下And, Or操作符…

反射(reflection)详细讲解

反射(reflection) 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息&#xff08;比如成员变量&#xff0c;构造器&#xff0c;成员方法等等&#xff09;&#xff0c;并能操作对象的属性及方法。反射在设计模式和框架底层都会用到加载完类之后&#xff0c;在堆…

【博弈论笔记】第四章 重复博弈

文章目录 第四章 重复博弈4.1 重复博弈引论4.1.1 重复博弈定义和意义4.1.2 重复博弈的基本概念 4.2 有限次重复博弈4.2.1 两人零和博弈的有限次重复博弈4.2.2 唯一纯策略纳什均衡博弈的有限次重复博弈4.2.3 多个纯策略纳什均衡博弈的有限次重复博弈4.2.4 有限次重复博弈的民间定…

Python学习笔记(3)--字符串定义、拼接、格式化,表达式格式化,数据输入,布尔数据类型,比较运算符

传送门>B站黑马python入门教程 目录 1.字符串定义方式2.字符串拼接3.字符串格式化3.1 常用占位符3.2 格式化时的数字精度控制3.3 快速格式化字符串 4. 表达式格式化5. 数据输入-input语句6.布尔数据类型、比较运算符 1.字符串定义方式 在 python 语法中,字符串有三种定义方式…

Python爬虫 从小白到高手 Urllib

Urllib 1.什么是互联网爬虫&#xff1f; 如果我们把互联网比作一张大的蜘蛛网&#xff0c;那一台计算机上的数据便是蜘蛛网上的一个猎物&#xff0c;而爬虫程序就是一只小蜘蛛&#xff0c;沿着蜘蛛网抓取自己想要的数据 解释1&#xff1a;通过一个程序&#xff0c;根据Url(http…

行为型模式--访问者模式

目录 概述 结构 案例实现 优缺点 优点&#xff1a; 缺点&#xff1a; 使用场景 概述 封装一些作用于某种数据结构中的各元素的操作&#xff0c;它可以在不改变这个数据结构的前提下定义作用于这 些元素的新的操作。 结构 访问者模式包含以下主要角色: 抽象访问者&am…

采用SpringBoot+Tinymce实现文章的在线预览和上传

采用SpringBootTinymce实现文章的在线预览和上传 资源在gitee中 输入信息 预览

canvas详解09-像素操作

到目前为止,我们尚未深入了解 Canvas 画布真实像素的原理,事实上,你可以直接通过 ImageData 对象操纵像素数据,直接读取或将数据数组写入该对象中。稍后我们也将深入了解如何控制图像使其平滑(反锯齿)以及如何从 Canvas 画布中保存图像。 #ImageData 对象 ImageData对象…

el-table渲染二级对象数组

1、序言 项目地址如下&#xff1a;https://gitee.com/liu-wenxin/complexELTable.git 想要渲染这样的数据&#xff1a; el-table官网给的例子都是一级对象数组&#xff0c;如果想要渲染二级对象数组&#xff0c;直接 :table tableData 这样el-table渲染是不成功的&am…

RadEx Pro处理电火花数据操作步骤(下)

RadEx Pro处理电火花数据操作步骤&#xff08;上&#xff09;主要讲述RadEx Pro读取电火花数据&#xff0c;查看数据的质量&#xff0c;以及简单的滤波和振幅纠正。 6、海底拾取&#xff0c;建立流程060 seafloor pick Trace Input加载stack数据集 Trace Header Math&#x…

【人工智能】— 维度灾难、降维、主成分分析PCA、获取旧数据、非线性主成分分析

【人工智能】— 维度灾难、降维、主成分分析PCA、获取旧数据、非线性主成分分析 高维数据与维度灾难维度灾难降维为什么需要降维&#xff1f;PRINCIPLE COMPONENT ANALYSIS主成分的几何图像最小化到直线距离的平方和举例主成分的代数推导优化问题计算主成分&#xff08;Princip…

【Pandas】pandas用法解析(上)

目录 一、生成数据表 1.导入pandas库 2.导入CSV或者xlsx文件 3.用pandas创建数据表 二、数据表信息查看 1.维度查看 2.数据表基本信息&#xff08;维度、列名称、数据格式、所占空间等&#xff09; 3.每一列数据的格式 4.某一列格式 5.空值判断 6.查看某一列空值 7…

黑马程序员前端 Vue3 小兔鲜电商项目——(七)详情页

文章目录 路由配置模板代码配置路由链接跳转 渲染基础数据封装接口渲染数据 热榜区域模板代码封装接口渲染数据 图片预览组件封装小图切换大图显示模版代码绑定事件 放大镜效果图片优化 SKU组件熟悉全局组件统一插件化插件化开发插件注册 路由配置 模板代码 创建 src\views\D…

快速排序-详解附Python代码

排序思路 取一个元素P&#xff08;第一个元素&#xff09;&#xff0c;目标是使得元素P归位&#xff1b;列表被元素P分成了两个部分&#xff0c;左边的比P小&#xff0c;右边的比P大&#xff1b;分别再对左右两个部分的列表重复1&#xff0c;2步骤&#xff0c;递归完成排序 评…