从零搭建若依(Ruoyi-Vue)管理系统(10)--Spring Security核心内容梳理

本章是Spring Security理论和概念的东西,没有实际的搭建产出,为下节做个铺垫。只是介绍Spring Security一些核心要用到的东西,Spring Security的功能还是很强大的,有兴趣可以系统的学习和了解

历史遗留TODO:

  • 第四章
  1. 登录日志还未实现。(到登录和权限模块完成)
  2. LogAspect从缓存获取当前的用户信息使用模拟的数据(到登录和权限模块完成)

本章将留下TODO:

本章将解决TODO:

1. 选型原因

Spring Security:是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。

具有以下优点:

  1. Spring Security基于Spring开发,项目如果使用Spring作为基础,配合Spring Security做权限更加方便,社区资源十分丰富!
  2. 具有丰富全面的安全相关特性,业务隔离度好。

2.Spring Secutity核心内容

2.1 Spring Secutity中的用户信息

  1. UserDetailsService

image-20210311114229376

该方法很容易理解: 通过用户名来加载用户 。这个方法主要用于从系统数据中查询并加载具体的用户到 Spring Security中。

在开发中我们一般定义一个这个接口的实现类,自定义loadUserByUsername方法,实现从数据源获取用户,加载用户信息。也可以在其中实现一些校验用户逻辑。

例如:

自己Test中的例子:

image-20210311123710120

若依中的使用:

image-20210311123951593

  1. UserDetails:

从上面 UserDetailsService 可以知道最终交给Spring Security的是UserDetails 。该接口是提供用户信息的核心接口。该接口实现仅仅存储用户的信息。后续会将该接口提供的用户信息封装到认证对象 Authentication 中去。

UserDetails中默认提供:

  • 用户的权限集, 默认需要添加 ROLE_ 前缀
  • 用户的加密后的密码, 不加密会使用 {noop} 前缀
  • 应用内唯一的用户名
  • 账户是否过期
  • 账户是否锁定
  • 凭证是否过期
  • 用户是否可用

在这里插入图片描述

在我们自己的项目中,我们要定义个用户类实现该接口,在该用户类中我们可以扩展更多的用户信息,比如手机、邮箱等等

  1. UserDetailsServiceAutoConfiguration:

(若依中未使用)

源码:

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({AuthenticationManager.class})
@ConditionalOnBean({ObjectPostProcessor.class})
@ConditionalOnMissingBean(
    value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class},
    type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector"}
)
public class UserDetailsServiceAutoConfiguration {
    private static final String NOOP_PASSWORD_PREFIX = "{noop}";
    private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");
    private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);

    public UserDetailsServiceAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        type = {"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository"}
    )
    @Lazy
    public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {
        User user = properties.getUser();
        List<String> roles = user.getRoles();
        return new InMemoryUserDetailsManager(new UserDetails[]{org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});
    }

    private String getOrDeducePassword(User user, PasswordEncoder encoder) {
        String password = user.getPassword();
        if (user.isPasswordGenerated()) {
            logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
        }

        return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;
    }
}

我们来简单解读一下该类,从 @Conditional 系列注解我们知道,该类在类路径下存在 AuthenticationManager 或者在Spring 容器中存在Bean ObjectPostProcessor 并且不存在Bean AuthenticationManager , AuthenticationProvider , UserDetailsService 的情况下生效。 千万 不要纠结这些类干嘛用的! 该类只初始化了一个 UserDetailsManager 类型的Bean。 UserDetailsManager 类型负责对安全用户实体抽象 UserDetails 的增删查改操作。同时还继承了 UserDetailsService 接口。

image-20210311122045350

明白了上面这些让我们把目光再回到 UserDetailsServiceAutoConfiguration 上来。该类初始化了 一个名为 InMemoryUserDetailsManager 的内存用户管理器。该管理器通过配置注入了一个默认的 UserDetails 存在内存中,在项目使用中就是我们上面自己实现的继承UserDetails的User类 ,每次启动 user 都是动态生成的。

我们定义自己的 UserDetailsManager Bean就可以实现我们需要的用户管理逻辑

Spring Secutity提供了JdbcUserDetailsManager,该类继承 UserDetailsManager实现基于JDBC的用户管理逻辑。

(但是如果如此使用,就相当于UserDetails 对应一个数据库的表,但它其实不是一个实体类(enyity),只是一个业务对象(bo)。实际使用中我们还是有个User类对应数据库表,通过User类的Service操作用户数据)

2.2 密码加密

  1. 在Spring Security中有一个密码编码解码器PasswordEncoder接口,并且有多个代表用不同加密算法的实现类
image-20210311173648226

默认的加密算法是bcrypt,对应BCryptPasswordEncoder

  1. 其中DelegatingPasswordEncoder委托密码编码器

所含内容如图:

image-20210311174618699

  • idForEncode:通过id来匹配编码器,该id不能是 {} 包括的

  • DelegatingPasswordEncoder::初始化传入,用来提供默认的密码编码器。

  • passwordEncoderForEncode:通过上面 idForEncode 所匹配到的 PasswordEncoder 用来对密码进行编码 。

  • upgradeEncoding密码升级。

  • idToPasswordEncoder:用来维护多个 idForEncode 与具体 PasswordEncoder 的映射关系。 DelegatingPasswordEncoder 初始化时 装载进去,会在初始化时进行一些规则校验。

  • PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder():默认的密码匹配器,上面的 Map idToPasswordEncoder中都不存在就用它来执行 matches 方法进行匹配验证。这是一个内部类实现。

  • encode编码方法

    @Override
    public String encode(CharSequence rawPassword) {
       return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword);
    }
    

    从上面源码可以看出来通过 DelegatingPasswordEncoder 编码后的密码是遵循一定的规则的,遵循 {idForEncode}encodePassword 。也就是前缀 {} 包含了编码的方式再拼接上该方式编码后的密码串。注意:对相同字符串每次加密生成的结果都不同!

    例如:(12345678用bcrypt算法编码后的结果:)

    12345678  --(bcrypt)-->  {bcrypt}$2a$10$XEBcwHqwLYXrbyqN2r9T2..dTmpv23RKi4SxAc4vyyt7ZZh30slAy
    ```
    
    
  • matches:密码匹配方法:

    @Override
    public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
       if (rawPassword == null && prefixEncodedPassword == null) {
          return true;
       }
       String id = extractId(prefixEncodedPassword);
       PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
       if (delegate == null) {
          return this.defaultPasswordEncoderForMatches
             .matches(rawPassword, prefixEncodedPassword);
       }
       String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
       return delegate.matches(rawPassword, encodedPassword);
    }
    

    密码匹配通过传入原始密码和遵循 {idForEncode}encodePassword 规则的密码编码串。通过获取编 码方式id (idForEncode) 来从 DelegatingPasswordEncoder 中的映射集合 idToPasswordEncode 中获取具体的 PasswordEncoder 进行匹配校验。找不到就使用 UnmappedIdPasswordEncoder

  1. PasswordEncoderFactories 密码器静态工厂

制造 PasswordEncoder 。而且还是个静态工厂只提供了初始化 DelegatingPasswordEncoder 的方法。该类的主要方法:

@SuppressWarnings("deprecation")
public static PasswordEncoder createDelegatingPasswordEncoder() {
   String encodingId = "bcrypt";
   Map<String, PasswordEncoder> encoders = new HashMap<>();
   encoders.put(encodingId, new BCryptPasswordEncoder());
   encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
   encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
   encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
   encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
   encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
   encoders.put("scrypt", new SCryptPasswordEncoder());
   encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
   encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
   encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
   encoders.put("argon2", new Argon2PasswordEncoder());

   return new DelegatingPasswordEncoder(encodingId, encoders);
}

从上面可以非常具体地看出来 DelegatingPasswordEncoder提供的密码编码方式。默认采用了 bcrypt 进行编码。

因此,在 DelegatingPasswordEncoder 中也可以实现新旧密码加密校验方式同时存在,并且实现自动更新老密码的编码方式。

例如:旧密码编码方式是SHA-1,新的方式是bcrypt。这时候验证时,对于未登录的过的密码用新编码方式编然后存入数据库;老用户在数据库中存储的密码是用老编码方式编码的,这时候校验时,可以用老编码方式校验,成功验证后,把老密码再用新编码方式存入数据库。

示例:

在这里插入图片描述

2.3 Spring Security的配置

  1. SpringBootWebSecurityConfiguration:
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({WebSecurityConfigurerAdapter.class})
@ConditionalOnMissingBean({WebSecurityConfigurerAdapter.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
public class SpringBootWebSecurityConfiguration {
    public SpringBootWebSecurityConfiguration() {
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @Order(2147483642)
    static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
        DefaultConfigurerAdapter() {
        }
    }
}

这个类是Spring Security 对 Spring Boot Servlet Web 应用的默认配置。核心在于 WebSecurityConfigurerAdapter 适配器。从 @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class) 我们就能看出 WebSecurityConfigurerAdapter 是安全配置的核心。 默认情况下 DefaultConfigurerAdapter 将以 SecurityProperties.BASIC_AUTH_ORDER (-5 ) 的顺序注入 Spring IoC 容器,这是个空实现。 如果我们需要实现对Spring Security的配置可以通过继承 WebSecurityConfigurerAdapter 来实现。

在若依中:

image-20210311203225792

  1. 自定义SecurityConfig

上面也提到首先要继承WebSecurityConfigurerAdapter,其次最常用的是实现configure(AuthenticationManagerBuilder auth)configure(WebSecurity web)configure(HttpSecurity http)三个方法实现我们对Spring Security的自定义安全配置。

image-20210311204523623

  • void configure(AuthenticationManagerBuilder auth) 用来配置认证管理器 AuthenticationManager

  • void configure(WebSecurity web)用来配置 WebSecurity 。而 WebSecurity 是基于Servlet Filter用来配置 springSecurityFilterChain 。而 springSecurityFilterChain 又被委托给了 Spring Security 核心过滤器 Bean DelegatingFilterProxy 。 相关逻辑你可以在 WebSecurityConfiguration 中找到。我们一般不会过多来自定义 WebSecurity , 使用较多的使其 ignoring() 方法用来忽略 Spring Security 对静态资源的控制。

  • void configure(HttpSecurity http) 这个是我们使用最多的,用来配置 HttpSecurityHttpSecurity 用于构建一个安全过滤器链 SecurityFilterChain SecurityFilterChain 最终 被注入核心过滤器 。HttpSecurity 有许多我们需要的配置。我们可以通过它来进行自定义安全访问策略。

2.4 认证过程

在这里插入图片描述

(看mk网教程的一张图)

  1. 认证过滤器UsernamePasswordAuthenticationFilter:它的作用是拦截登录请求并获取账号和 密码,然后把账号密码封装到认证凭据 UsernamePasswordAuthenticationToken 中,然后把凭据交 给特定配置的 AuthenticationManager 去作认证。

image-20210312074036145

​ (图片来源《Spring Security实战干货》)

理解了 UsernamePasswordAuthenticationFilter 工作流程后可以做这些事情:

  • 定制我们的登录请求URI和请求方式。

  • 登录请求参数的格式定制化,比如可以使用 JSON格式提交甚至几种并存。

  • 将用户名和密码封装入凭据 UsernamePasswordAuthenticationToken ,定制业务场景需要的特殊凭据。

  1. 认证管理器AuthenticationManager:这个接口方法非常奇特,入参和返回值的类型都是 Authentication 。该接口的作用是对用户的未授信凭据进行认证,认证通过则返回授信状态的凭据,否则将抛出认证异常AuthenticationException

认证过程:

AuthenticationManager 的实现 ProviderManager 管理了众多的 AuthenticationProvider 。每 一个 AuthenticationProvider 都只支持特定类型的 Authentication ,然后是对适配到的 Authentication 进行认证,只要有一个 AuthenticationProvider 认证成功,那么就认为认证成功,所有的都没有通过才认为是认证失败。认证成功后的 Authentication 就变成授信凭据,并触发认证成功的事件。认证失败的就抛出异常触发认证失败的事件。

在这里插入图片描述

认证管理器 AuthenticationManager 针对特定的 Authentication 提供了特定的 认证功能,我们可以借此来实现多种认证并存(多因子登录)。

2.5 过滤器和过滤链

Spring Security 以一个单 Filter(FilterChainProxy) 存在于整个过滤器链中,而 这个 FilterChainProxy 实际内部代理着众多的 Spring Security Filter 。

在这里插入图片描述

Spring Security 内置过滤器:

  • ChannelProcessingFilter:通常是用来过滤哪些请求必须用 https 协议, 哪些请求必须用 http 协议,哪些请求随便用哪个协议都行。
  • ConcurrentSessionFilter:主要用来判断 session 是否过期以及更新最新的访问时间。
  • WebAsyncManagerIntegrationFilter:用于集成SecurityContext到Spring异步执行机制中的 WebAsyncManager。用来处理异步请求的安全上下文。
  • SecurityContextPersistenceFilter:主要控制 SecurityContext 的在一次请求中的生命周期 。 请求来临时,创建 SecurityContext 安全上下文信息,请求结束时清空 SecurityContextHolder 。
  • HeaderWriterFilter: HeaderWriterFilter 用来给 http 响应添加一些 Header ,比如 X-Frame-Options , X-XSS- Protection ,X-Content-Type-Options 。
  • CorsFilter:跨域相关的过滤器。这是 Spring MVC Java 配置和 XML 命名空间 CORS 配置的替代方法, 仅对依赖 于 spring-web 的应用程序有用(不适用于 spring-webmvc )或 要求在 javax.servlet.Filter 级别 进行CORS检查的安全约束链接。
  • CsrfFilter:用于防止 csrf 攻击,前后端使用json交互需要注意的一个问题。
  • LogoutFilter:处理注销的过滤器。
  • OAuth2AuthorizationRequestRedirectFilter:这个需要依赖 spring-scurity-oauth2 相关的模块。该过滤器是处理 OAuth2 请求首选重定向相关逻辑的。
  • Saml2WebSsoAuthenticationRequestFilter:这个需要用到 Spring Security SAML 模块,这是一个基于SMALSSO 单点登录请求认证过滤器。
  • X509AuthenticationFilter: X509 认证过滤器。
  • AbstractPreAuthenticatedProcessingFilter: 处理经过预先认证的身份验证请求的过滤器的 基类其中认证主体已经由外部系统进行了身份验证。目的只是从传入请求中提取主体上的必要信息, 而不是对它们进行身份验证。可以继承该类来具体实现并通过HttpSecurity.addFilter 方法来添加自定义 AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter: CAS 单点登录认证过滤器 。依赖 Spring Security CAS 模块。
  • OAuth2LoginAuthenticationFilter:这个需要依赖 spring-scurity-oauth2 相关的模块。 OAuth2 登录认证过滤器。处理通过 OAuth2 进行认证登录的逻辑。
  • Saml2WebSsoAuthenticationFilter:这个需要用到 Spring Security SAML 模块,这是一个基于 SMAL 的SSO 单点登录认证过滤器。
  • UsernamePasswordAuthenticationFilter:处理用户以及密码认证的核心过滤器。认证请求提交的 username 和 password ,被封装成 token 进行一系列的认证,便是主要通过这个过滤器完成的,在 表单认证的方法中,这是最最关键的过滤器。
  • OpenIDAuthenticationFilter:基于 OpenID 认证协议的认证过滤器。 你需要在依赖中依赖额外的相关模块才能启用它。
  • DefaultLoginPageGeneratingFilter:生成默认的登录页。默认 /login
  • DefaultLogoutPageGeneratingFilter:生成默认的退出页。 默认/logout
  • DigestAuthenticationFilter: Digest 身份验证是 Web 应用程序中流行的可选的身份验证机制 。 DigestAuthenticationFilter 能够处理 HTTP 头中显示的摘要式身份验证凭据。
  • BasicAuthenticationFilter: Digest 身份验证是 Web 应用程序中流行的可选的身份验证机制 。验证处理Basic Auth HTTP头中的数据。
  • RequestCacheAwareFilter:用于用户认证成功后,重新恢复因为登录被打断的请求。当匿名访问一个需要授权的资源时。会跳转到 认证处理逻辑,此时请求被缓存。在认证逻辑处理完毕后,从缓存中获取最开始的资源请求进行再次请 求。
  • SecurityContextHolderAwareRequestFilter:用来 实现 j2ee Servlet Api 一些接口方法, 比如 getRemoteUser 方法、isUserInRole 方法, 在使用 Spring Security 时其实就是通过这个过滤器来实现的。
  • JaasApiIntegrationFilter:适用于 JAAS (Java 认证授权服务)。 如果 SecurityContextHolder 中拥有的 Authentication 是一个JaasAuthenticationToken,那么该 JaasApiIntegrationFilter 将使用包含在 JaasAuthenticationToken 中的 Subject 继续执行 FilterChain
  • RememberMeAuthenticationFilter: 处理 记住我 功能的过滤器。
  • AnonymousAuthenticationFilter:匿名认证过滤器。 对于 Spring Security 来说,所有对资源的访问都是有 Authentication 的。对于无需登录(UsernamePasswordAuthenticationFilter )直接可以访问的资源,会授予其匿名用 户身份 。
  • SessionManagementFilter: Session 管理器过滤器,内部维护了一个SessionAuthenticationStrategy 用于管理 Session 。
  • ExceptionTranslationFilter:主要来传输异常事件。
  • FilterSecurityInterceptor:这个过滤器决定了访问特定路径应该具备的权限,访问的用户的角色,权限是什么?访问的路径需要什 么样的角色和权限?这些判断和处理都是由该类进行的。
  • SwitchUserFilter: 用来做账户切换的。默认的切换账号的 url 为 /login/impersonate ,默认注 销切换账号的 url 为 /logout/impersonate ,默认的账号参数为 username 。
    你可以通过此类实现自定义的账户切换。

向项目中添加过滤器:

在配置文件的configure(HttpSecurity httpSecurity)方法中:

在这里插入图片描述

4个方法分别是:addFilter–添加过滤器;addFilterAfter–把过滤器添加到某过滤器之后;addFilterAt–替代某过滤器;addFilterBefore–把过滤器添加到某过滤器之前

2.6 权限相关

一、基于配置表达式控制 URL 路径

在继承WebSecurityConfigurerAdapter 的配置类中的configure(HttpSecurity http)中进行配置。

例如:

protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("admin")
            .antMatchers("/user/**").hasAnyRole("admin", "user")
            .anyRequest().authenticated()
            .and()
            ...
}

常用的配置可选项:

表达式备注
hasRole用户具备某个角色即可访问资源
hasAnyRole用户具备多个角色中的任意一个即可访问资源
hasAuthority类似于 hasRole
hasAnyAuthority类似于 hasAnyRole
permitAll统统允许访问
denyAll统统拒绝访问
isAnonymous判断是否匿名用户
isAuthenticated判断是否认证成功
isRememberMe判断是否通过记住我登录的
isFullyAuthenticated判断是否用户名/密码登录的
principle当前用户
authentication从 SecurityContext 中提取出来的用户对象

二、基于注解的接口权限控制

我们可以在任何 @Configuration 实例上使用 @EnableGlobalMethodSecurity 注解来启用全局方 法安全注解功能

例如:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    ...
}
  • 设置 prePostEnabled 为 true ,则开启了基于表达式 的方法安全控制。

这个配置开启了四个注解,分别是:

  1. @PreAuthorize:在标记的方法调用之前,通过表达式来计算是否可以授权访问。

示例:

@Service
public class TestService {
	// 只有当前登录用户名为 Gangbb 的用户才可以访问该方法。
    @PreAuthorize("principal.username.equals('Gangbb')")
    public String hello() {
        return "hello";
    }
    
    //用户名开头为 Gangbb 的用户才能访问。
    @PreAuthorize("principal.username.startsWith('Gangbb')")
    public String hello() {
        return "hello";
    }
	
	// 访问该方法的用户必须具备 ROLE_ADMIN 角色。
    @PreAuthorize("hasRole('ADMIN')")
    public String admin() {
        return "admin";
    }
	//*******************以下是基于SpEL 表达式****************
    
	// 表示访问该方法的 age 参数必须大于 98,否则请求不予通过。
    @PreAuthorize("#age>98")
    public String getAge(Integer age) {
        return String.valueOf(age);
    }
    
    //  入参id 必须同当前的用户名相同。
    @PreAuthorize("#id.equals(principal.username)")
    public String getId(Integer age) {
        return String.valueOf(age);
    }
    
    //......更多关于SpEL 表达式可参考官方文档
}
  1. @PostAuthorize:在标记的方法调用之后,通过表达式来计算是否可以授权访问。该注解是针对 @PreAuthorize 。区别 在于先执行方法。而后进行表达式判断。如果方法没有返回值实际上等于开放权限控制;如果有返回值 实际的结果是用户操作成功但是得不到响应。

  2. @PreFilter:基于方法入参相关的表达式,对入参进行过滤。

  3. @PostFilter:和 @PreFilter 不同的是, 基于返回值相关的表达式,对返回值进行过滤。分页慎用! 该过程发生接口进行数据返回之前。

示例:

测试数据: [“Gangbb”, “GangAA”, “Hangbb”] 都有ROLE_ADMIN 角色

// filterObject表示要过滤的元素对象。 如下会过滤掉 Gangbb GangAA,只通过 Hangbb
@PreFilter(value = "filterObject.startsWith('G')",filterTarget = "ids") 

// 如下配置都会通过。如果都没有ROLE_ADMIN 角色则过滤掉Hangbb
@PreFilter("hasRole('ADMIN') or filterObject.startsWith('H')")

// 对集合进行过滤,只返回后缀为 2 的元素
@PostFilter("filterObject.lastIndexOf('2')!=-1")
public List<String> getAllUser() {
    List<String> users = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        users.add("javaboy:" + i);
    }
    return users;
}

// 由于有两个集合,因此使用 filterTarget 指定过滤对象。
@PreFilter(filterTarget = "ages",value = "filterObject%2==0")
public void getAllAge(List<Integer> ages,List<String> users) {
    System.out.println("ages = " + ages);
    System.out.println("users = " + users);
}

  • 设置 securedEnabled 为 true ,就开启了角色注解 @Secured ,该注解功能要简单的多,默认情况下只能基于角色(默认需要带前缀 ROLE_ )集合来进行访问控制决策。
    该注解的机制是只要其声明的角色集合(value )中包含当前用户持有的任一角色就可以访问。也就是 用户的角色集合和@Secured注解的角色集合要存在非空的交集。 不支持使用 SpEL 表达式进行决策。

  • 设置 jsr250Enabled 为 true ,就开启了 JavaEE 安全 注解中的以下三个:

  1. @DenyAll 拒绝所有的访问
  2. @PermitAll 同意所有的访问
  3. @RolesAllowed 用法和@Secured 一样。

三、动态权限控制

这个若依中未有使用,也挺复杂,后续有时间再详细分析。

  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
RuoYi若依管理系统是一个基于SpringBoot的权限管理系统,代码易读易懂、界面简洁美观。 核心技术采用Spring、MyBatis、Shiro没有任何其它重度依赖。直接运行即可用。 RuoYi若依管理系统功能: 1、用户管理:用户是系统操作者,该功能主要完成系统用户配置。 2、部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持权限。 3、岗位管理:配置系统用户所属担任职务。 4、菜单管理:配置系统菜单,操作权限,按钮权限标识等。 5、角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 6、字典管理:对系统中经常使用的一些较为固定的数据进行维护。 7、参数管理:对系统动态配置常用参数。 8、通知公告:系统通知公告信息发布维护。 9、操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 10、登录日志:系统登录日志记录查询包含登录异常。 11、在线用户:当前系统中活跃用户状态监控。 12、定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 13、代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 14、系统接口:根据业务代码自动生成相关的api接口文档。 15、服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 16、在线构建器:拖动表单元素生成相应的HTML代码。 17、连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。   RuoYi若依管理系统 更新日志: v4.6.1 新增IE浏览器版本过低提示页面 新增详细信息tab页签方式 新增解锁屏幕打开上次页签 数据监控默认账户密码防止越权访问 新增表格示例(导出选择列) 个人信息添加手机&邮箱重复验证 个人中心刷新后样式问题 操作日志返回参数添加非空验证 velocity剔除commons-collections版本,防止3.2.1版本的反序列化漏洞 子表模板默认日期格式化 代码生成预览语言根据后缀名高亮显示 代码生成主子表相同字段导致数据问题 升级SpringBoot到最新版本2.2.13 升级shiro到最新版1.7.1 阻止身份认证绕过漏洞 升级bootstrapTable到最新版本v1.18.2 升级bootstrapTable相关组件到最新版本v1.18.2 升级fastjson到最新版1.2.75 升级druid到最新版本v1.2.4 升级oshi到最新版本v5.6.0 修改ip字段长度防止ipv6地址长度不够 搜索建议示例选择后隐藏列表 主子表示例增加初始化数据 优化Excel导入增加空行判断 修复横向菜单无法打开页签问题 修复导入数据为负浮点数时,导入结果会丢失精度问题 优化更多操作按钮左侧移入内容闪现消失情况 修复主子表提交中列隐藏后出现列偏移问题 单据打印网页时通过hidden-print隐藏元素 表格销毁清除记住选择数据 增加表格动态列示例 代码生成选择主子表关联元素必填 tree根据Id和Name选中指定节点增加空判断 其他细节优化
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了若依管理系统。她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。所有前端后台代码封装过后十分精简易上手,出错效率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。 您是否在找一套合适后台管理系统。 您是否在找一套代码易读易懂后台管理系统。 那么,现在若依来了。诚意奉献之作 若依是给刚出生的女儿取的名字 寓意:你若不离不弃,我必生死相依 内置功能 用户管理:用户是系统操作者。 部门管理:配置系统组织机构。 岗位管理:岗位是用户所属职务。 菜单管理:配置系统菜单(支持控制到按钮)。 角色管理:角色菜单权限分配。 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 操作日志:系统操作日志记录(含异常)。 登录日志:系统登录情况记录(含异常)。 在线用户:当前系统中活跃用户状态监控。 连接池监视:监视当期系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 为何选择若依 是一个完全响应式,基于Bootstrap3.3.6最新版本开发的主题。 她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。 拥有良好的代码结构,层次结构清晰。内置一系列基础功能。 操作权限控制精密细致,对所有管理链接都进行权限验证,可控制到按钮。 提供在线功能代码生成工具,提高开发效率及质量。 提供常用工具类封装,日志、国际化、缓存、验证、字典等数据。 兼容目前最流行浏览器(IE7+、Chrome、Firefox)手机移动端也支持。 技术选型 1、后端 核心框架:Spring Boot 安全框架:Apache Shiro 模板引擎:Thymeleaf 持久层框架:MyBatis 数据库连接池:Druid 缓存框架:Ehcache 日志管理:SLF4J 工具类:Apache Commons Fastjson POJO:Lombok 2、前端 框架:Bootstrap 数据表格:Bootstrap Table 客户端验证:JQuery Validation 树结构控件:zTree 弹出层:layer 3、平台 服务器中间件:SpringBoot内置 数据库支持:目前仅提供MySql数据库的支持,但不限于数据库 开发环境:Java、Eclipse、Maven、Git
RuoYi-Vue的版本采用了Spring Security来进行权限认证。 Spring Security是一个强大且广泛使用的安全框架,它提供了一套全面的认证和授权机制,可以确保系统只有经过身份验证的用户才能访问受保护的资源。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [RuoYi-Vue-Oracle(RuoYi):基于SpringBoot的官方仓库,Spring Security,JWT,Vue&Element的前替代分离...](https://download.csdn.net/download/weixin_42133918/15399484)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [RuoYi-Vue-Multi-Tenant:基于RuoYi-Vue扩展的多租户框架(SpringBootSpring Security,JWT,Vue&Element...](https://download.csdn.net/download/weixin_42118160/16271786)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [ruoyi-vue-pro:基于SpringBootSpring Security,JWT,Vue&Element的前一次分离权限管理系统](https://download.csdn.net/download/weixin_42166626/15607582)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值