【Spring Security】根据请求路径判断当前登录用户是否拥有访问权限

背景

一个系统要做好权限控制是非常重要的一件事情,不仅要判断当前请求的用户是否已经登录,还要判断当前用户是否拥有访问某个接口的权限,这样才能防止纵向越权。最典型的场景,就是普通用户不能访问管理员的接口,首先大家能想到的一个解决方案,就是根据角色去判断,这确实是其中一个解决方案,并且Spring Security也提供了@PreAuthorize系列注解直接加到接口上,就可以根据角色进行控制。

使用注解也有一个缺点,那就是需要在N多接口上加注解,那有没有不使用注解的一种全局通用的解决方案呢?答案是肯定的。本文主要从Spring Security提供的机制上,找出一个最佳实现,不仅可以根据角色判断,也能通过请求路径进行判断。因为角色的粒度还是比较粗,在权限体系里面,很多时候一条权限数据,就对应后端的一个接口,比如新增是一个权限,删除是一个权限。那么每个接口都有一个请求路径,我们可以根据这个请求路径进行更细粒度的权限控制。

本文开发环境介绍

开发依赖版本
Spring Boot3.0.6

pom.xml依赖

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

主角AuthorizationManager登场

AccessDecisionManager已经@Deprecated,建议使用AuthorizationManager取代。这是一个接口,主要实现check方法AuthorizationDecision check(Supplier<Authentication> authentication, T object);,如果有权限,则返回一个带true的授权决策return new AuthorizationDecision(true);;如果没有权限,则返回一个带false的授权决策return new AuthorizationDecision(false);

动手实现一个DemoAuthorizationManager

源码如下

package com.wen3.oauth.ss.authclient.authorization;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.util.function.Supplier;

@RequiredArgsConstructor
@Component
@Slf4j
public class DemoAuthorizationManager<T> implements AuthorizationManager<T> {

    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
    private final HttpServletRequest httpServletRequest;

    @Override
    public AuthorizationDecision check(Supplier<Authentication> supplier, T object) {
        Authentication authentication = supplier.get();
        log.info("authentication: {}", authentication);
        log.info("object: {}", object);
        boolean isAnonymous = authentication != null && !this.trustResolver.isAnonymous(authentication)
                && authentication.isAuthenticated();

        if(!isAnonymous) {
            return new AuthorizationDecision(false);
        }

        String servletPath = httpServletRequest.getServletPath();
        log.info("servletPath: {}", servletPath);
        // TODO: 判断当前用户是否拥有访问servletPath的权限
        boolean granted = true;

        return new AuthorizationDecision(granted);
    }
}

让DemoAuthorizationManager生效

package com.wen3.oauth.ss.authclient.autoconfigure;

import com.wen3.oauth.ss.authclient.handler.DemoBearerTokenAuthenticationEntryPoint;
import com.wen3.oauth.ss.authclient.processor.OAuth2AuthorizationRequestRedirectFilterPostProcessor;
import com.wen3.oauth.ss.authclient.processor.OAuth2LoginAuthenticationFilterPostProcessor;
import com.wen3.oauth.ss.authclient.processor.SpringOpaqueTokenIntrospectorPostProcessor;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
public class Oauth2ClientAutoConfiguration {

    @Resource
    private AuthorizationManager authorizationManager;

    @Bean
    public SecurityFilterChain authorizationClientSecurityFilterChain(HttpSecurity http) throws Exception {
        AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizationManagerRequestMatcherRegistry =  http.authorizeHttpRequests();
        authorizationManagerRequestMatcherRegistry.requestMatchers(
                new AntPathRequestMatcher("/authorized"),
                new AntPathRequestMatcher("/error"),
                new AntPathRequestMatcher("/webjars/**"),
                new AntPathRequestMatcher("/resources/**"),
                new AntPathRequestMatcher("/index/**"),
                new AntPathRequestMatcher("/**/*.css"),
                new AntPathRequestMatcher("/**/*.ico")
        ).permitAll();
        authorizationManagerRequestMatcherRegistry.anyRequest().access(authorizationManager);

        http.formLogin();
        return http.build();
    }
}
  • 关键生效的一行是`authorizationManagerRequestMatcherRegistry.anyRequest().access(authorizationManager);
  • 这样就可以在DemoAuthorizationManager自定义我们的权限判断逻辑,角色也好,请求路径也好,或者其它特定需求都可以定制开发了

总结

Spring Security功能很强大,对安全认证这一块提供了非常丰富的支持,篇幅有限,只是演示了非常基础的功能。相信通过这篇文章的介绍,可以帮助大家解决一定的困惑。

本人对Spring Security的研究非常深入,几乎翻看了底层所有的源码,熟悉Spring Security运行的机制、原理,如果大家在使用Spring Security的过程中遇到什么难题,欢迎进一步沟通、交流。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 好的,我会尽量详细地讲解使用 Spring Security 和 Redis 实现用户登录权限控制的细节。 首先,你需要在你的项目中引入 Spring Security 和 Redis 的依赖。 在配置文件中,你需要配置 Redis 的连接信息,并且指定使用 Redis 作为 Spring Security 的缓存。 然后,你需要定义一个 UserDetailsService,它负责从数据库中读取用户登录信息。在这个类中,你可以使用 RedisTemplate 将用户信息缓存到 Redis 中。 接着,你需要配置 Spring Security 的认证流程。这包括指定使用哪种认证方式(比如表单认证),以及如何处理登录请求登录成功/失败的情况。 最后,你还需要配置权限控制。这包括指定哪些路径需要身份认证和授权,以及如何检查用户是否具有访问某个路径权限。 在这些配置完成之后,当用户尝试访问受保护的路径时,Spring Security 会自动拦截请求并进行身份认证和权限检查。如果用户身份认证成功且具有访问路径权限,就会放行请求;否则, ### 回答2: 使用Spring Security和Redis实现用户登录以及权限控制主要涉及以下几个方面的细节: 1. 用户认证:用户输入用户名和密码后,Spring Security会将其传递给一个认证管理器(AuthenticationManager),该管理器会根据用户提供的信息进行认证。在认证过程中,可以使用Redis作为缓存存储用户信息,提高认证的效率。 2. 密码加密:为了增强安全性,用户的密码通常应该经过加密处理后才进行存储。Spring Security提供了多种加密方式,如BCrypt、SHA等,可以根据项目需求选择适合的加密方式。在存储用户密码时,可以使用Redis的存储功能进行持久化存储。 3. 授权管理:一旦用户通过认证,Spring Security会为该用户分配相应的权限。可以将用户权限信息存储在Redis中,方便后续的权限控制操作。通过配置合适的访问规则,可以根据用户的角色或权限对不同的资源进行访问控制。 4. Session管理:在用户登录后,系统会为用户生成一个会话(Session),用于记录用户登录状态。可以将会话信息存储在Redis中,由Spring Security进行管理。通过Redis的分布式存储特性,可以实现多个应用服务器之间的会话共享,增强系统的可扩展性和容错性。 使用Spring Security和Redis实现用户登录以及权限控制的细节可以通过使用Spring Security提供的相关注解和配置来完成。这些注解和配置包括用户认证的自定义逻辑、密码加密配置、权限配置、会话管理配置等。同时,为了实现与Redis的集成,可以使用Spring Data Redis提供的工具类和注解,简化与Redis的交互操作。通过合理的配置和编码实现,可以保障系统的安全性和可扩展性。 ### 回答3: 使用Spring Security和Redis实现用户登录权限控制的过程可以分为以下几个细节。 首先,我们需要在Spring Boot项目中配置Spring Security和Redis的相关依赖。在pom.xml文件中添加相应的依赖,并进行配置。 接下来,我们需要创建一个用户模型,包含字段如用户名、密码和权限等。可以使用Spring Data JPA来管理用户模型的持久化和CRUD操作。 在用户登录过程中,首先用户需要提供用户名和密码。Spring Security可以提供默认的登录表单页面,也可以自定义登录页面。用户提交登录请求后,Spring Security会拦截该请求,并通过验证用户名和密码的方式,验证用户身份的合法性。这可以通过自定义UserDetailsService来实现,UserDetailsService可以从数据库(MySQL等)中获取用户信息,用于验证用户身份。当验证成功后,Spring Security会生成一个Token,并将其存储到Redis中。 在权限控制方面,用户登录后,可以通过访问需要权限的接口或资源。Spring Security可以通过对应的注解,如@PreAuthorize、@PostAuthorize等,在方法级别或类级别上添加权限控制规则。这些注解可以用来定义访问某个接口或资源需要的权限。当用户访问某个需要权限的接口时,Spring Security会通过Token从Redis中获取用户信息,并根据用户权限与接口所需权限进行对比。如果用户拥有足够的权限,就可以访问接口;否则,将会返回403错误。 另外,在权限控制方面,可以利用Spring Security提供的@PreAuthorize注解进行动态权限控制,即根据用户当前权限动态决定用户是否可以访问某个接口。这可以通过在注解中添加SpEL表达式来实现,例如:@PreAuthorize("hasAnyAuthority('admin','user')"),表示只有拥有admin或user权限用户才能够访问该接口。 综上所述,使用Spring Security和Redis实现用户登录权限控制,通过验证用户身份和对用户权限进行控制,可以保障系统的安全性和权限管理。该方案具备灵活性,并且可以与其他框架和工具(如Spring Cloud等)结合使用,实现更加完整的分布式系统的用户登录权限控制功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太空眼睛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值