Spring Security 权限控制

news2025/1/20 12:09:27

日积月累,水滴石穿 😄

前言

项目版本
Boot2.3.12.RELEASE
Security5.3.9.RELEASE

官网文档

在前面的文章中,所有的接口只需要登录就能访问。并没有对每个接口进行权限限制。 在正式的系统中,一个用户会拥有一个或者多个角色,而不同的角色会拥有不同的接口权限。如果要实现这些功能,需要重写WebSecurityConfigurerAdapter 中的configure(HttpSecurity http)。HttpSecurity 用于构建一个安全过滤器链 SecurityFilterChain,可以通过它来进行自定义安全访问策略。

配置如下:


@Bean
PasswordEncoder passwordEncoder() {
    return NoOpPasswordEncoder.getInstance();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
            .withUser("cxyxj")
            .password("123").roles("admin", "user")
            .and()
            .withUser("security")
            .password("security").roles("user");
}

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()  //开启配置
            .antMatchers("/cxyxj/**").hasRole("admin")   //访问/cxyxj/**下的路径,必须具备admin身份
            .antMatchers("/security/**").hasRole("user") //访问/security/**下的路径,必须具备user身份
            .antMatchers("/permitAll").permitAll() // 访问/permitAll路径,不需要登录
            .anyRequest() //其他请求
            .authenticated()//验证   表示其他请求只需要登录就能访问
            .and() 
            .formLogin(); // 开启表单登陆
}
复制代码

上述配置含义如下:

  • antMatchers("/cxyxj/**").hasRole("admin"):表示访问/cxyxj/路径的必须要有 admin 角色。
  • antMatchers("/security/**").hasRole("user"):表示访问/security/路径的必须要有 user 角色。
  • .antMatchers("/permitAll").permitAll():表示访问/permitAll 路径不需要认证
  • .anyRequest().authenticated():表示除了前面定义的url,其余url访问都得认证后才能访问(登录)
  • and:表示结束当前标签,回到上下文 HttpSecurity,开启新一轮的配置
  • formLogin:开启表单登陆

根据上述的配置,我们新增三个接口,代码如下:


@GetMapping("/hello") 
public String hello(){ 
    return "你好 Spring Security"; 
}

@GetMapping("/cxyxj/hello")
public String cxyxj() {
    return "cxyxj 你好 Spring Security";
}

@GetMapping("/security/hello")
public String user() {
    return "user 你好 Spring Security";
}


@GetMapping("/permitAll")
public String permitAll() {
    return "permitAll 你好 Spring Security";
}
复制代码

按照我们的配置,permitAll接口不需要登录就能访问,cxyxj接口只有admin角色才能访问,security接口admin角色和user角色都能访问,而 hello接口登录就能访问!

这里就不演示了,各位可以将上述代码 copy 到本地运行一下。

授权方式

授权的方式包括 web授权方法授权,web授权是通过 url 拦截进行授权,方法授权是通过方法拦截进行授权。他们都会使用 AccessDecisionManager接口进行授权决策。若为web授权则拦截器为 FilterSecurityInterceptor;若为方法授权则拦截器为MethodSecurityInterceptor。如果同时使用 web 授权和方法授权,则先执行web授权,再执行方法授权,最后决策都通过,则允许访问资源,否则将禁止访问。 文章开头使用的方式就是 web授权方式

  • FilterSecurityInterceptor:底层是 Filter。

  • MethodSecurityInterceptor:底层是 AOP。

web授权

Spring Security 可以通过 http.authorizeRequests() 开启对 web 请求进行授权保护。

http.formLogin();
http.authorizeRequests()
        .antMatchers("/permitAll").permitAll() // 访问/permitAll路径,不需要认证
        .anyRequest()   //其他请求
        .authenticated(); //需要认证才能访问
复制代码

url匹配

antMatchers()

方法定义如下:

antMatchers(String... antPatterns) {}
复制代码

参数是可变长参数,每个参数是一个 ant 表达式,用于匹配 URL规则。

ANT通配符有三种:

通配符说明
?匹配任何单字符
*匹配0个或者任意数量的字符
**匹配0个或者任意目录
// 访问 /cxyxj/** 路径,任意目录下 .js 文件 可以直接访问
.antMatchers("/cxyxj/**","/**/*.js").permitAll()
复制代码

使用 antMatchers 方法需要注意配置规则的顺序,配置顺序会影响授权的效果,越是具体的应该放在前面,越是笼统的应该放到后面。

如下错误示例:

.antMatchers("/cxyxj/**").hasRole("ADMIN") 
.antMatchers("/cxyxj/login").permitAll()
复制代码

如上配置会导致访问/cxyxj/login接口时需要拥有ADMIN角色才能访问。

regexMatchers

使用正则表达式进行匹配。

//所有以.js 结尾的文件都被放行 
.regexMatchers( ".+[.]js").permitAll()
复制代码

无论是 antMatchers() 还是 regexMatchers() 都具有两个参数的方法,其中第一个参数都是 HttpMethod ,表示请求方式,当设置了 HttpMethod 后表示只有设定的请求方式才执行对应的权限验证。

.antMatchers(HttpMethod.GET,"/cxyxj/hello").permitAll() 
.regexMatchers(HttpMethod.GET,".+[.]jpg").permitAll()
复制代码

anyRequest

匹配所有的请求。该方法一般会放在最后。结合 authenticated使用,表示所有请求需要认证才能访问。

.anyRequest().authenticated()
复制代码

mvcMatchers

适用于配置了 mvcServletPath 的情况。 mvcServletPath 就是所有的 URL 的统一前缀。在 application.properties 中添加下面内容。

spring.mvc.servlet.path= /role
复制代码
  • 正例
.mvcMatchers("/cxyxj/**").servletPath("/role").permitAll()
复制代码
  • 反例
.mvcMatchers("/role/cxyxj/**").permitAll()
复制代码

如果不想使用 mvcMatchers() 也可以使用 antMatchers()。

.antMatchers("/role/cxyxj/**").permitAll()
复制代码

如果你在项目中还配置了项目根路径。

server.servlet.context-path=/ctx-path
复制代码

SecurityConfig中不需要理会,你只需要修改你的请求接口路径。

  • 配置
.mvcMatchers("/cxyxj/**").servletPath("/role").permitAll()
复制代码
  • 请求接口路径
http://localhost:8080/ctx-path/role/cxyxj/hello
复制代码

调试完成之后请将properties文件的配置进行注释!以下示例代码不使用配置文件。

RequestMatcher接口

上述的几种匹配方式都是 RequestMatcher 接口的子实现。 接口定义了matches方法,方法如果返回 true 表示提供的请求与提供的匹配规则匹配,如果返回的是 false 则不匹配。 Spring Security 内置提供了一些 RequestMatcher 实现类:

内置操作

上文只是将请求接口路径与配置的规则进行匹配,那匹配成功之后应该进行什么操作呢?Spring Security 内置了一些控制操作。

  • permitAll() 方法,所有用户可访问。
  • denyAll() 方法,所有用户不可访问。
  • authenticated() 方法,登录用户可访问。
  • anonymous() 方法,匿名用户可访问。
  • rememberMe() 方法,通过 remember me 登录的用户可访问。
  • fullyAuthenticated() 方法,非 remember me 登录的用户可访问。
  • hasIpAddress(String ipaddressExpression) 方法,来自指定 IP 表达式的用户可访问。
  • hasRole(String role) 方法, 拥有指定角色的用户可访问,传入的角色将被自动增加 “ROLE_” 前缀。
  • hasAnyRole(String... roles) 方法,拥有指定任意角色的用户可访问。传入的角色将被自动增加 “ROLE_” 前缀。
  • hasAuthority(String authority) 方法,拥有指定权限( authority )的用户可访问。
  • hasAuthority(String... authorities) 方法,拥有指定任意权限( authority )的用户可访问。
  • access(String attribute) 方法,上面所有方法的底层实现,当 Spring EL 表达式的执行结果为 true 时,可以访问。 使用用法如下:
// 如果用户具备 admin 权限,就允许访问。
 .antMatchers("/cxyxj/**").hasAuthority("admin") 
// 如果用户具备给定权限中某一个,就允许访问。
.antMatchers("/admin/demo").hasAnyAuthority("admin","System")
// 如果用户具备 user 权限,就允许访问。注意不需要手动写 ROLE_ 前缀,写了会报错
.antMatchers("/security/**").hasRole("user") 
//如果请求是指定的 IP 就允许访问。
.antMatchers("/admin/demo").hasIpAddress("192.168.64.5")
复制代码

这里单独介绍一下 access(表达式)。表达式的基类是SecurityExpressionRoot,提供了一些在web和方法安全性中都可用的通用表达式,如下:

表达式描述
hasRole(String role)当前用户是否拥有指定角色。拥有则可以访问。
hasAnyRole(String... roles)当前用户是否拥有指定角色中的任意一个,拥有则可以访问。
hasAuthority(String authority)拥有指定权限( authority )的用户可访问。
hasAnyAuthority(String... authorities)拥有指定任一权限( authority )的用户可访问。
getPrincipal获得当前用户,可能是一个用户名,也可能是一个用户对象。
getAuthentication获取当前Authentication对象(认证对象)。
permitAll总是返回true,表示允许所有用户访问。
denyAll总是返回false,表示拒绝所有用户访问。
isAnonymous()是否是一个匿名用户,如果是则允许访问。
isRememberMe()用户是否是通过Remember-Me自动登录,如果是则允许访问。
isAuthenticated()用户是否登录认证成功,如果是则允许访问。
isFullyAuthenticated()如果当前用户不是一个匿名用户,又不是通过Remember-Me自动登录的,则允许访问。(手动输入帐户信息认证)。

可以使用 access() 实现相同的功能。

.antMatchers("/cxyxj/**").access("hasAuthority('admin')")
.antMatchers("/permitAll").access("isAnonymous")
复制代码

自定义方法(重要)

虽然内置了很多的表达式,但是在实际项目中,用的很少,而且很有可能不能满足需求。所以需要自定义逻辑的情况。比如判断当前登录用户是否具有访问当前 URL 的权限!

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

@Component
public class MyAccess {
// 只需要登录就能访问的接口地址
    private static final Set<String> URL = new HashSet<>();
 // 需要区分角色的接口地址  用户名称:接口地址
    private static final HashMap<String,Set<String>> URL_MAP = new HashMap();
    static {
        URL.add("/hello");

        Set<String> cxyxjSet = new HashSet<>();
        cxyxjSet.add("/cxyxj/hello");
        cxyxjSet.add("/security/hello");
        URL_MAP.put("cxyxj",cxyxjSet);
        
        Set<String> securitySet = new HashSet<>();
        securitySet.add("/security/hello");
        URL_MAP.put("security",securitySet);
    }

   public boolean hasPermit(HttpServletRequest req, Authentication auth){
       Object principal = auth.getPrincipal();
       String servletPath = req.getRequestURI();
       AntPathMatcher matcher = new AntPathMatcher();
       // 有一些接口是不需要权限,只要登录就能访问的,比如一些省市区接口
       boolean result = URL.stream().anyMatch(url -> matcher.match(url, servletPath));
       if(result){
           return true;
       }
       //这里使用的是定义在内存的用户信息
       if(principal instanceof User){
           User user = (User) principal;
           // 可以根据用户id或者用户名从redis中获得用户拥有的菜单权限url
           String username = user.getUsername();
           Set<String> urlSet = URL_MAP.get(username);
           return urlSet.stream().anyMatch(u -> matcher.match(u, servletPath));
       }
       return false;
   }

}
复制代码
  • 配置
 @Override
   protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()  //开启配置
                .antMatchers("/permitAll").permitAll()  // 访问/permitAll路径,不需要登录
                .anyRequest().access("@myAccess.hasPermit(request,authentication)")
                //.anyRequest() //其他请求
                //.authenticated()//验证   表示其他请求只需要登录就能访问
                .and()
                .formLogin();
    }
复制代码

使用这种方式,authenticated(),就不需要加了。access表达式的写法为 @符号 + Bean名称.方法(参数...)

  • 增加方法
 @GetMapping("/denyAll")
 public String denyAll() {
     return "denyAll 你好 Spring Security";
 }
复制代码
  • 测试 登录 cxyxj用户:可以访问/cxyxj/hello/security/hellohellopermitAll接口。不能访问 denyAll接口。

登录 security用户:可以访问/security/hellohellopermitAll接口。不能访问 denyAll/cxyxj/hello接口。

方法授权

Spring Security在方法的权限控制上支持三种类型的注解,JSR-250注解、@Secured注解、支持表达式注解。这三种注解默认都是没有启用的,需要使用@EnableGlobalMethodSecurity来进行启用。

1、JSR250E

@EnableGlobalMethodSecurity 设置 jsr250Enabled 为 true ,就开启了以下三个安全注解:

  • @RolesAllowed:表示访问对应方法时应该具备所指定的角色。示例:@RolesAllowed({"user", "admin"}),表示该方法只要具有"user", "admin"任意一种权限就可以访问。可以省略前缀ROLE_不写。该注解可以标注在类上,也可以标注在方法上,当标注在类上时表示类中所有方法的执行都需要对应的角色,当标注在方法上表示执行该方法时所需要的角色,当方法和类上都标注了@RolesAllowed,则方法上的@RolesAllowed将覆盖类上的@RolesAllowed。

  • @PermitAll 表示允许所有的角色进行访问。@PermitAll可以标注在方法上也可以标注在类上,当标注在方法上时则只对对应方法不进行权限控制,而标注在类上时表示对类里面所有的方法都不进行权限控制。

    • (1)当 @PermitAll 标注在类上,而 @RolesAllowed 标注在方法上时,@RolesAllowed 将覆盖 @PermitAll,即需要 @RolesAllowed 对应的角色才能访问。
    • (2)当 @RolesAllowed 标注在类上,而 @PermitAll 标注在方法上时则对应的方法不进行权限控制。
    • (3)当在类和方法上同时使用了@PermitAll 和 @RolesAllowed 时,先定义的将发生作用。
  • @DenyAll 表示什么角色都不能访问。@DenyAll可以标注在方法上也可以标注在类上,当标注在方法上时则只对对应方法进行权限控制,而标注在类上时表示对类里面所有的方法都进行权限控制。

提供测试接口

@GetMapping("/hello")
public String hello(){
    return "你好 Spring Security";
}

@GetMapping("/cxyxj/hello")
@RolesAllowed({"admin"})
public String cxyxj() {
    return "cxyxj 你好 Spring Security";
}


@GetMapping("/security/hello")
@RolesAllowed({"user"})
public String user() {
    return "user 你好 Spring Security";
}


@GetMapping("/denyAll")
@DenyAll
public String denyAll() {
    return "denyAll 你好 Spring Security";
}


@GetMapping("/permitAll")
@PermitAll
public String permitAll() {
    return "permitAll 你好 Spring Security";
}
复制代码

SecurityConfig

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

    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("cxyxj")
                .password("123").roles("admin", "user")
                .and()
                .withUser("security")
                .password("security").roles("user");
    }
  @Override
   protected void configure(HttpSecurity http) throws Exception {
       http.formLogin();
   }
}
复制代码

hello接口登录就能访问。/cxyxj/hello接口需要有 admin角色才能访问,/security/hello接口需要有 user角色才能访问,denyAll接口不允许访问,permitAll 接口所有人都能访问。

各位是不是发现 configure(HttpSecurity http)方法中只配置了 http.formLogin()。我为什么没有配置其他的呢?这是因为当配置 .authorizeRequests().anyRequest().authenticated()之后,所有方法需要认证之后才能访问。该配置与 @PermitAll注解发生了冲突。

开篇提到过:若为web授权则拦截器为 FilterSecurityInterceptor;若为方法授权则拦截器为MethodSecurityInterceptor。如果同时使用web 授权和方法授权,则先执行web授权,再执行方法授权,最后决策通过,则允许访问资源,否则将禁止访问。 当配置.authorizeRequests().anyRequest().authenticated()之后,相当于同时使用 web 授权和方法授权,然后被 web 授权拦截了,所以 @PermitAll 并没有生效。当然实际项目中, @PermitAll一般不会使用!

2、secured

@EnableGlobalMethodSecurity 设置 securedEnabled 为 true ,就开启了以下注解:

  • @Secured 是由 Spring Security 定义的,用来支持方法权限控制的注解。@Secured 专门用于判断是否具有指定角色的,可以写在方法或类上。参数需要手动指定 "ROLE_" 前缀。

SecurityConfig

@EnableGlobalMethodSecurity(jsr250Enabled = true,securedEnabled = true)
复制代码

提供测试接口

@GetMapping("/secured")
@Secured({"admin"})
public String secured() {
    return "secured 你好 Spring Security";
}
复制代码

3、表达式

Spring Security 中定义了四个支持使用表达式的注解,分别是 @PreAuthorize、@PostAuthorize、 @PreFilter 和 @PostFilter。其中前两者可以用来在方法调用前或者调用后进行权限检查,后两者可以用来对集合类型的参数或者返回值进行过滤。 接下来演示一下使用方式,首先我们先来开启注解,在 @EnableGlobalMethodSecurity 设置 prePostEnabled 为 true 。

SecurityConfig

@EnableGlobalMethodSecurity(jsr250Enabled = true,securedEnabled = true,prePostEnabled = true)
复制代码

@PreAuthorize

最被常用的注解为@PreAuthorize。在方法调用前进行权限检查,结果为 true 则可以执行!可以在类或者方法上进行标注。注解参数与 access方法参数一致,也就是说可以使用内置方法和Spring-EL表达式。

// 只有角色为  admin 或者 user才能访问
@PreAuthorize("hasRole('admin') or hasRole('user')")
@GetMapping("/preAuthorize")
public String preAuthorize(){
    return "PreAuthorize 你好 Spring Security";
}

//Id大于1才能查询
// 按名称访问任何方法参数作为表达式变量
@PreAuthorize("#id>1")
@GetMapping("/findById/{id}")
public Integer findById(@PathVariable("id") Integer id) {
  return id;
}

// 限制只能查询自己的信息
// principal 值通常是 UserDetails 实例
@PreAuthorize("principal.username.equals(#username)")
@GetMapping("/findByName/{username}")
public String findByName(@PathVariable("username") String username) {
    return username;
}
//限制只能新增用户名称为abc的用户
@PreAuthorize("#username.equals('abc')")
@GetMapping("/add/{username}")
public String add(@PathVariable("username") String username) {
    return username;
}
复制代码

@PostAuthorize

这个注解使用的很少,当然可能某些需求需要使用。比如需要校验该方法的返回值,这可以使用 @PostAuthorize 注解来实现。要访问方法的返回值,请使用内置表达式 returnObject。

@PostAuthorize("returnObject.id%2==0")
    @GetMapping("/find/{id}")
    public SysUser find(@PathVariable("id") Integer id) {
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        User principal = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        sysUser.setUsername(principal.getUsername());
        return sysUser;
}
复制代码

在方法调用完成后进行权限检查,如果返回值的id是偶数则表示校验通过,否则表示校验失败,将 抛出AccessDeniedException。@PostAuthorize是在方法调用完成后进行权限检查,它不能控制方法是否能被调用,只能在方法调用完成后,检查权限然后决定是否要抛出AccessDeniedException异常。

@PreFilter

对集合、数组类型的请求参数进行过滤,移除结果为false的元素。该过程发生在接口接收参数之前,可以使用内置表达式 filterObjec 进行数据过滤,filterObjec 表示集合中的当前对象。 如果有多个集合参数需要通过 filterTarget=<参数名> 来指定过滤的集合。

// id 大于 1 的才进行查询
@PreFilter("filterObject > 1")
@PostMapping("/find")
public List<Integer> batchGetInfo(@RequestParam("ids") ArrayList<Integer> ids) {
    return ids;
}
复制代码

多个集合参数需要通过 filterTarget=<参数名> 来指定。如下:

// id 大于 1 的才进行查询
@PreFilter(filterTarget = "ids",value = "filterObject > 1")
@PostMapping("/batchGetInfo")
public List<Integer> batchGetInfo(@RequestParam("ids") ArrayList<Integer> ids,@RequestParam("ids2")ArrayList<Integer> ids2) {
    return ids;
}
复制代码

@PostFilter

@PostFilter可以对集合、数组类型的返回值进行过滤!

// 将结果集为偶数的值进行返回
@PostFilter("filterObject % 2 == 0")
@GetMapping("/findAll")
public List<Integer> findAll() {
    List<Integer> userList = new ArrayList<>();
    for (int i=0; i<10; i++) {
        userList.add(i);
    }
    return userList;
}

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

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

相关文章

三种架构模式——MVC、MVP、MVVM

目录 前言 一、MVC&#xff08;Model-View-Controller&#xff09; 1、简介 2、框架图 二、MVP&#xff08;Model-View-Presenter&#xff09; 1、简介 2、框架图 三、MVVM&#xff08;Model-View-ViewModel&#xff09; 1、简介 2、框架图 四、总结 前言 MV系列框…

微信小程序插件--wxml-to-canvas(生成图片)

一、需求 项目中要实现一个将图片分享到朋友圈的功能&#xff0c;将生成的海报转成图片保存到手机。用到了wxml-to-canvas插件。 二、官方示例使用方法 1.安装wxml-to-canvas npm install --save wxml-to-canvas2.JSON 组件声明 {"usingComponents": {"wxml-t…

多款顶级好用的 Vue 表单设计器测评推荐,可拖拽生成表单

本文完整版&#xff1a;《多款顶级好用的 Vue 表单设计器测评推荐&#xff0c;可拖拽生成表单》 Vue 表单设计器form-generator - 适配 Element Plus UI 框架的表单设计器form-render - 阿里团队开源表单设计器&#xff0c;自家 Antd UI 框架友好form-create - 支持Vue3 及 Ele…

内网npm私有仓库搭建以及使用教程

前言 前端团队沉淀一套通用的UI库、工具类、脚手架&#xff0c;不允许在公网发布&#xff0c;内网npm私有库搭建需求应运而生。如何在内网环境搭建npm私有仓库并使用&#xff1f;主角登场了 —— Verdaccio。接下来我来教大家使用 verdaccio 在内网环境中搭建npm私有仓库。 基…

canvas详细入门教程(1W字 吐血分享)

大家好&#xff0c;我是潘潘 今天为大家带来的是我已经写了很久了的canvas详细教程&#xff0c;对入门canvas很有帮助。 点击跳转原文&#xff1a; canvas详细教程原文 canvas是什么&#xff1f; 简单来说&#xff0c;<canvas> 是HTML5中的标签&#xff0c;它是一个容…

20分钟学会flex布局,熊二都表示学会了,你呢?

✏️ 作者&#xff1a;大二计算机专业学生 ♉ 星座&#xff1a;金牛座 &#x1f3e0; 主页&#xff1a;查看更多文章 &#x1f3c2; 关键&#xff1a;flex 前端布局 熊二都会 大家好&#xff0c;我是小周&#xff0c;今天分享的是熊二都能学会的前端 flex 布局&#xff0c;篇幅…

小程序 getActivePinia was called with no active Pinia. Did you forget to install pinia?

小程序项目使用pinia做状态管理报错&#xff1a; Error: [&#x1f34d;]: getActivePinia was called with no active Pinia. Did you forget to install pinia? const pinia createPinia() app.use(pinia) app运行打包时有个同样的错误 错误原因是&#xff1a;在外部js/t…

Collections类详解

目录 一.Collections概述&#xff1a; 1.1什么是Collections类&#xff1a; 1.2 Collections类和collection的区别和联系&#xff1a; 二. Collections类的主要方法&#xff1a; 一.Collections概述&#xff1a; 1.1 什么是Collections类&#xff1a; Java.util.Collections…

【Node.js】Express框架的基本使用

✍️ 作者简介: 前端新手学习中。 &#x1f482; 作者主页: 作者主页查看更多前端教学 &#x1f393; 专栏分享&#xff1a;css重难点教学 Node.js教学 从头开始学习 目录 初识Express Express简介 什么是Express 进一步理解 Express Express能做什么 Express的基本使用 …

前端登录退出:处理Token问题(获取、缓存、失效处理)以及代码实现

目录一、什么是Token二、获取token三、Token失效处理注意点1、主动退出2、Token过期① 逻辑图②方案③代码实现3、被人顶号① 逻辑图② 方案③代码实现一、什么是Token Token是服务端生成的一串字符串&#xff0c;当用户第一次登陆成功后&#xff0c;服务器会生成一个token&am…

vue 实现自适应屏幕

1.安装自适应屏幕插件&#xff08;优先使用vscode安装&#xff0c;需要调整px转化rem比例&#xff09; &#xff08;1&#xff09;安装插件&#xff1a; npm i lib-flexible --save &#xff08;2&#xff09;mian.js引入&#xff1a; import lib-flexible/flexible.js 2. 如果…

java中controller层是干嘛的?

最近在研究开源框架的源码&#xff0c;打算改写开源框架&#xff0c;适用于自身的业务场景。于是找到了一个框架(spiderFlow)。 根据他的git上的教程&#xff0c;搭建了一个。 controller层&#xff0c;在我们写代码的时候&#xff0c;一般为接口层&#xff0c;与前端&#xf…

CSS锥形渐变实现环形进度条

10月份因为疫情原因、又开启了居家办公模式&#xff0c;空闲之余&#xff0c;与其选择“躺平”&#xff0c;不如去做一些有意义的事情&#xff0c;内心的想法驱使着我去做些什么&#xff0c;但是又没有合适的素材&#xff0c;直到接手了最近的一个可视化项目&#xff0c;一个图…

若依框架---权限管理设计

前言 若依权限管理包含两个部分&#xff1a;菜单权限 和 数据权限。菜单权限控制着我们可以执行哪些操作。数据权限控制着我们可以看到哪些数据。 菜单是一个概括性名称&#xff0c;可以细分为目录、菜单和按钮&#xff0c;以若依自身为例&#xff1a; 目录&#xff0c;就是页…

关于古老的jsp页面的知识汇总(超详细)

1. 为什么要开发出来jsp文件呢&#xff1f; 面对需要将大量的结果&#xff0c;甚至是一整个<html>页面返回给响应体&#xff0c;之前的方法就显得十分麻烦。 之前我们是这样将结果返回给响应体的&#xff0c;如下图&#xff1a; 于是就设计了jsp文件&#xff0c;用来解决…

antd upload上传格式.doc、.docx、.pdf、.png、.jpg、.rar和大小100兆限制

限制上传文件格式.rar、.zip、.pdf、.jpg、.png、.docx antd 中upload对于限制上传文件格式的属性是accept&#xff0c;在开发的过程中&#xff0c;accept对于.doc、.docx、.pdf、.png、.jpg、.rar&#xff0c;格式的限制是完全没有问题的。但是测试和我说&#xff0c;当选择文…

前端开发之vue-grid-layout的使用和实例

前端开发之vue-grid-layout的使用和实例前言效果图一、vue中简单案例1、安装组件NPMYarn2、vue文件二、vue3使用&#xff08;vue文件&#xff09;1、需要导入vue3支持的该版本插件2、在mian.js里引入&#xff1a;三、在IE上无法打开&#xff0c;并报错缺少&#xff1a;&#xf…

【TypeScript基础】TypeScript之常用类型(下)

前言 博主主页&#x1f449;&#x1f3fb;蜡笔雏田学代码 专栏链接&#x1f449;&#x1f3fb;【TypeScript专栏】 上篇文章讲解了TypeScript的一些常用类型&#xff0c;&#x1f449;&#x1f3fb;详细内容请阅读【TypeScript基础】TypeScript之常用类型&#xff08;上&#x…

解决TypeError: Cannot read properties of undefined (reading ‘NormalModule‘)的三种方案

目录 前言 第一种 第二种 第三种 前言 大家好呀&#xff01;我是爷爷的茶七里香&#xff0c;今天遇到了一件&#x1f95a;疼的事&#xff0c;一个vuevant写的APP&#xff0c;更换了电脑之后运行不起来&#xff0c;就很奇怪很离谱&#xff0c;报错信息如下&#xff1a; ER…

使用Iframe打印预览pdf,兼容谷歌火狐浏览器

功能场景&#xff1a; 点击页面的打印按钮&#xff0c;可以调用浏览器的打印功能&#xff0c;并预览打印pdf文件。解决火狐浏览器出现about:blank问题。 功能实现&#xff1a; 实现思路&#xff1a; 使用接口获取pdf文件的二进制流&#xff0c;响应类型为blob将拿到的二进制…