spring security——基本介绍(一)

一、spring security 简介

        spring security 的核心功能主要包括:

  • 认证 (你是谁)
  • 授权 (你能干什么)
  • 攻击防护 (防止伪造身份)

     其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。

比如,对于username password认证过滤器来说, 

会检查是否是一个登录请求;

是否包含username 和 password (也就是该过滤器需要的一些认证信息) ;

如果不满足则放行给下一个。

     下一个按照自身职责判定是否是自身需要的信息,basic的特征就是在请求头中有 Authorization:Basic eHh4Onh4 的信息。中间可能还有更多的认证过滤器。最后一环是 FilterSecurityInterceptor,这里会判定该请求是否能进行访问rest服务,判断的依据是 BrowserSecurityConfig中的配置,如果被拒绝了就会抛出不同的异常(根据具体的原因)。Exception Translation Filter 会捕获抛出的错误,然后根据不同的认证方式进行信息的返回提示。

注意:绿色的过滤器可以配置是否生效,其他的都不能控制。

二、入门项目

     首先创建spring boot项目HelloSecurity,其pom主要依赖如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

然后在src/main/resources/templates/目录下创建页面:

home.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example</title>
    </head>
    <body>
        <h1>Welcome!</h1>
​
        <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
    </body>
</html>

我们可以看到, 在这个简单的视图中包含了一个链接: “/hello”. 链接到了如下的页面,Thymeleaf模板如下:

hello.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1>Hello world!</h1>
    </body>
</html>

Web应用程序基于Spring MVC。 因此,你需要配置Spring MVC并设置视图控制器来暴露这些模板。 如下是一个典型的Spring MVC配置类。在src/main/java/hello目录下(所以java都在这里):

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/hello").setViewName("hello");
        registry.addViewController("/login").setViewName("login");
    }
}

     addViewControllers()方法(覆盖WebMvcConfigurerAdapter中同名的方法)添加了四个视图控制器。 两个视图控制器引用名称为“home”的视图(在home.html中定义),另一个引用名为“hello”的视图(在hello.html中定义)。 第四个视图控制器引用另一个名为“login”的视图。 将在下一部分中创建该视图。此时,可以跳过来使应用程序可执行并运行应用程序,而无需登录任何内容。然后启动程序如下:

@SpringBootApplication
public class Application {
​
    public static void main(String[] args) throws Throwable {
        SpringApplication.run(Application.class, args);
    }
}

2、加入Spring Security

     假设你希望防止未经授权的用户访问“/ hello”。 此时,如果用户点击主页上的链接,他们会看到问候语,请求被没有被拦截。 你需要添加一个障碍,使得用户在看到该页面之前登录。您可以通过在应用程序中配置Spring Security来实现。 如果Spring Security在类路径上,则Spring Boot会使用“Basic认证”来自动保护所有HTTP端点。 同时,你可以进一步自定义安全设置。首先在pom文件中引入:

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

如下是安全配置,使得只有认证过的用户才可以访问到问候页面:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }
​
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }
}

     WebSecurityConfig类使用了@EnableWebSecurity注解 ,以启用Spring Security的Web安全支持,并提供Spring MVC集成。它还扩展了WebSecurityConfigurerAdapter,并覆盖了一些方法来设置Web安全配置的一些细节。

     configure(HttpSecurity)方法定义了哪些URL路径应该被保护,哪些不应该。具体来说,“/”和“/ home”路径被配置为不需要任何身份验证。所有其他路径必须经过身份验证。

     当用户成功登录时,它们将被重定向到先前请求的需要身份认证的页面。有一个由 loginPage()指定的自定义“/登录”页面,每个人都可以查看它。

     对于configureGlobal(AuthenticationManagerBuilder) 方法,它将单个用户设置在内存中。该用户的用户名为“user”,密码为“password”,角色为“USER”。

     现在我们需要创建登录页面。前面我们已经配置了“login”的视图控制器,因此现在只需要创建登录页面即可:

login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example </title>
    </head>
    <body>
        <div th:if="${param.error}">
            Invalid username and password.
        </div>
        <div th:if="${param.logout}">
            You have been logged out.
        </div>
        <form th:action="@{/login}" method="post">
            <div><label> User Name : <input type="text" name="username"/> </label></div>
            <div><label> Password: <input type="password" name="password"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>

     你可以看到,这个Thymeleaf模板只是提供一个表单来获取用户名和密码,并将它们提交到“/ login”。 根据配置,Spring Security提供了一个拦截该请求并验证用户的过滤器。 如果用户未通过认证,该页面将重定向到“/ login?error”,并在页面显示相应的错误消息。 注销成功后,我们的应用程序将发送到“/ login?logout”,我们的页面显示相应的登出成功消息。最后,我们需要向用户提供一个显示当前用户名和登出的方法。 更新hello.html 向当前用户打印一句hello,并包含一个“注销”表单,如下所示:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Sign Out"/>
        </form>
    </body>
</html>

三、参数详解

1、注解 @EnableWebSecurity

     在 Spring boot 应用中使用 Spring Security,用到了 @EnableWebSecurity注解,官方说明为,该注解和 @Configuration 注解一起使用, 注解 WebSecurityConfigurer 类型的类,或者利用@EnableWebSecurity 注解继承 WebSecurityConfigurerAdapter的类,这样就构成了 Spring Security 的配置。

2、抽象类 WebSecurityConfigurerAdapter

     一般情况,会选择继承 WebSecurityConfigurerAdapter 类,其官方说明为:WebSecurityConfigurerAdapter 提供了一种便利的方式去创建 WebSecurityConfigurer的实例,只需要重写 WebSecurityConfigurerAdapter 的方法,即可配置拦截什么URL、设置什么权限等安全控制。

3、方法 configure(AuthenticationManagerBuilder auth) 和 configure(HttpSecurity http)

     Demo 中重写了 WebSecurityConfigurerAdapter 的两个方法:

   /**
     * 通过 {@link #authenticationManager()} 方法的默认实现尝试获取一个 {@link AuthenticationManager}.
     * 如果被复写, 应该使用{@link AuthenticationManagerBuilder} 来指定 {@link AuthenticationManager}.
     *
     * 例如, 可以使用以下配置在内存中进行注册公开内存的身份验证{@link UserDetailsService}:
     *
     * // 在内存中添加 user 和 admin 用户
     * @Override
     * protected void configure(AuthenticationManagerBuilder auth) {
     *     auth
     *       .inMemoryAuthentication().withUser("user").password("password").roles("USER").and()
     *         .withUser("admin").password("password").roles("USER", "ADMIN");
     * }
     *
     * // 将 UserDetailsService 显示为 Bean
     * @Bean
     * @Override
     * public UserDetailsService userDetailsServiceBean() throws Exception {
     *     return super.userDetailsServiceBean();
     * }
     *
     */
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        this.disableLocalConfigureAuthenticationBldr = true;
    }


    /**
     * 复写这个方法来配置 {@link HttpSecurity}. 
     * 通常,子类不能通过调用 super 来调用此方法,因为它可能会覆盖其配置。 默认配置为:
     * 
     * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
     *
     */
    protected void configure(HttpSecurity http) throws Exception {
        logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
​
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin().and()
            .httpBasic();
    }

4、final 类 HttpSecurity

HttpSecurity 常用方法及说明:

方法说明
openidLogin()用于基于 OpenId 的验证
headers()将安全标头添加到响应
cors()配置跨域资源共享( CORS )
sessionManagement()允许配置会话管理
portMapper()允许配置一个PortMapper(HttpSecurity#(getSharedObject(class))),其他提供SecurityConfigurer的对象使用 PortMapper 从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security使用一个PortMapperImpl映射 HTTP 端口8080到 HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443
jee()配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理
x509()配置基于x509的认证
rememberMe允许配置“记住我”的验证
authorizeRequests()允许基于使用HttpServletRequest限制访问
requestCache()允许配置请求缓存
exceptionHandling()允许配置错误处理
securityContext()HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。 当使用WebSecurityConfigurerAdapter时,这将自动应用
servletApi()HttpServletRequest方法与在其上找到的值集成到SecurityContext中。 当使用WebSecurityConfigurerAdapter时,这将自动应用
csrf()添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用
logout()添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success”
anonymous()允许配置匿名用户的表示方法。 当与WebSecurityConfigurerAdapter结合使用时,这将自动应用。 默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,并包含角色 “ROLE_ANONYMOUS”
formLogin()指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面
oauth2Login()根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证
requiresChannel()配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射
httpBasic()配置 Http Basic 验证
addFilterAt()在指定的Filter类的位置添加过滤器

5、类 AuthenticationManagerBuilder

/**
* {@link SecurityBuilder} used to create an {@link AuthenticationManager}. Allows for
* easily building in memory authentication, LDAP authentication, JDBC based
* authentication, adding {@link UserDetailsService}, and adding
* {@link AuthenticationProvider}'s.
*/

        意思是,AuthenticationManagerBuilder 用于创建一个 AuthenticationManager,让我能够轻松的实现内存验证、LADP验证、基于JDBC的验证、添加UserDetailsService、添加AuthenticationProvider。

四、原理讲解

1、校验流程图

 

2、源码分析

  • AbstractAuthenticationProcessingFilter 抽象类
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
​
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
​
        if (!requiresAuthentication(request, response)) {
            chain.doFilter(request, response);
​
            return;
        }
​
        if (logger.isDebugEnabled()) {
            logger.debug("Request is to process authentication");
        }
​
        Authentication authResult;
​
        try {
            authResult = attemptAuthentication(request, response);
            if (authResult == null) {
                // return immediately as subclass has indicated that it hasn't completed
                // authentication
                return;
            }
            sessionStrategy.onAuthentication(authResult, request, response);
        }
        catch (InternalAuthenticationServiceException failed) {
            logger.error(
                    "An internal error occurred while trying to authenticate the user.",
                    failed);
            unsuccessfulAuthentication(request, response, failed);
​
            return;
        }
        catch (AuthenticationException failed) {
            // Authentication failed
            unsuccessfulAuthentication(request, response, failed);
​
            return;
        }
​
        // Authentication success
        if (continueChainBeforeSuccessfulAuthentication) {
            chain.doFilter(request, response);
        }
​
        successfulAuthentication(request, response, chain, authResult);
    }

        调用 requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。如果需要验证,则会调用 attemptAuthentication(HttpServletRequest, HttpServletResponse) 方法,有三种结果:

  1. 返回一个 Authentication 对象。配置的 SessionAuthenticationStrategy` 将被调用,然后 然后调用 successfulAuthentication(HttpServletRequest,HttpServletResponse,FilterChain,Authentication) 方法。
  2. 验证时发生 AuthenticationException。unsuccessfulAuthentication(HttpServletRequest, HttpServletResponse, AuthenticationException) 方法将被调用。
  3. 返回Null,表示身份验证不完整。假设子类做了一些必要的工作(如重定向)来继续处理验证,方法将立即返回。假设后一个请求将被这种方法接收,其中返回的Authentication对象不为空。
  • UsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter的子类)
public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
​
        String username = obtainUsername(request);
        String password = obtainPassword(request);
​
        if (username == null) {
            username = "";
        }
​
        if (password == null) {
            password = "";
        }
​
        username = username.trim();
​
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);
​
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
​
        return this.getAuthenticationManager().authenticate(authRequest);
    }

        attemptAuthentication () 方法将 request 中的 username 和 password 生成 UsernamePasswordAuthenticationToken 对象,用于 AuthenticationManager 的验证(即 this.getAuthenticationManager().authenticate(authRequest) )。默认情况下注入 Spring 容器的 AuthenticationManager 是 ProviderManager。

  • ProviderManager(AuthenticationManager的实现类)
public Authentication authenticate(Authentication authentication)
    throws AuthenticationException {
    Class<? extends Authentication> toTest = authentication.getClass();
    AuthenticationException lastException = null;
    Authentication result = null;
    boolean debug = logger.isDebugEnabled();
​
    for (AuthenticationProvider provider : getProviders()) {
        if (!provider.supports(toTest)) {
            continue;
        }
​
        if (debug) {
            logger.debug("Authentication attempt using "
                         + provider.getClass().getName());
        }
​
        try {
            result = provider.authenticate(authentication);
​
            if (result != null) {
                copyDetails(authentication, result);
                break;
            }
        }
        catch (AccountStatusException e) {
            prepareException(e, authentication);
            // SEC-546: Avoid polling additional providers if auth failure is due to
            // invalid account status
            throw e;
        }
        catch (InternalAuthenticationServiceException e) {
            prepareException(e, authentication);
            throw e;
        }
        catch (AuthenticationException e) {
            lastException = e;
        }
    }
​
    if (result == null && parent != null) {
        // Allow the parent to try.
        try {
            result = parent.authenticate(authentication);
        }
        catch (ProviderNotFoundException e) {
            // ignore as we will throw below if no other exception occurred prior to
            // calling parent and the parent
            // may throw ProviderNotFound even though a provider in the child already
            // handled the request
        }
        catch (AuthenticationException e) {
            lastException = e;
        }
    }
​
    if (result != null) {
        if (eraseCredentialsAfterAuthentication
            && (result instanceof CredentialsContainer)) {
            // Authentication is complete. Remove credentials and other secret data
            // from authentication
            ((CredentialsContainer) result).eraseCredentials();
        }
​
        eventPublisher.publishAuthenticationSuccess(result);
        return result;
    }
​
    // Parent was null, or didn't authenticate (or throw an exception).
​
    if (lastException == null) {
        lastException = new ProviderNotFoundException(messages.getMessage(
            "ProviderManager.providerNotFound",
            new Object[] { toTest.getName() },
            "No AuthenticationProvider found for {0}"));
    }
​
    prepareException(lastException, authentication);
​
    throw lastException;
}

        尝试验证 Authentication 对象。AuthenticationProvider 列表将被连续尝试,直到 AuthenticationProvider 表示它能够认证传递的过来的Authentication 对象。然后将使用该 AuthenticationProvider 尝试身份验证。如果有多个 AuthenticationProvider 支持验证传递过来的Authentication 对象,那么由第一个来确定结果,覆盖早期支持AuthenticationProviders 所引发的任何可能的AuthenticationException。 成功验证后,将不会尝试后续的AuthenticationProvider。如果最后所有的 AuthenticationProviders 都没有成功验证 Authentication 对象,将抛出 AuthenticationException。从代码中不难看出,由 provider 来验证 authentication, 核心点方法是:

Authentication result = provider.authenticate(authentication);

此处的 provider 是 AbstractUserDetailsAuthenticationProvider,AbstractUserDetailsAuthenticationProvider 是AuthenticationProvider的实现,看看它的 authenticate(authentication) 方法:

// 验证 authentication
public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
                messages.getMessage(
                        "AbstractUserDetailsAuthenticationProvider.onlySupports",
                        "Only UsernamePasswordAuthenticationToken is supported"));
​
        // Determine username
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                : authentication.getName();
​
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);
​
        if (user == null) {
            cacheWasUsed = false;
​
            try {
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            catch (UsernameNotFoundException notFound) {
                logger.debug("User '" + username + "' not found");
​
                if (hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(messages.getMessage(
                            "AbstractUserDetailsAuthenticationProvider.badCredentials",
                            "Bad credentials"));
                }
                else {
                    throw notFound;
                }
            }
​
            Assert.notNull(user,
                    "retrieveUser returned null - a violation of the interface contract");
        }
​
        try {
            preAuthenticationChecks.check(user);
            additionalAuthenticationChecks(user,
                    (UsernamePasswordAuthenticationToken) authentication);
        }
        catch (AuthenticationException exception) {
            if (cacheWasUsed) {
                // There was a problem, so try again after checking
                // we're using latest data (i.e. not from the cache)
                cacheWasUsed = false;
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
                preAuthenticationChecks.check(user);
                additionalAuthenticationChecks(user,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            else {
                throw exception;
            }
        }
​
        postAuthenticationChecks.check(user);
​
        if (!cacheWasUsed) {
            this.userCache.putUserInCache(user);
        }
​
        Object principalToReturn = user;
​
        if (forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }
​
        return createSuccessAuthentication(principalToReturn, authentication, user);
    }

AbstractUserDetailsAuthenticationProvider 内置了缓存机制,从缓存中获取不到的 UserDetails 信息的话,就调用如下方法获取用户信息,然后和 用户传来的信息进行对比来判断是否验证成功。

// 获取用户信息
UserDetails user = retrieveUser(username,
 (UsernamePasswordAuthenticationToken) authentication);

retrieveUser() 方法在 DaoAuthenticationProvider 中实现,DaoAuthenticationProvider 是 AbstractUserDetailsAuthenticationProvider的子类。具体实现如下:

protected final UserDetails retrieveUser(String username,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        UserDetails loadedUser;
​
        try {
            loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        }
        catch (UsernameNotFoundException notFound) {
            if (authentication.getCredentials() != null) {
                String presentedPassword = authentication.getCredentials().toString();
                passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,
                        presentedPassword, null);
            }
            throw notFound;
        }
        catch (Exception repositoryProblem) {
            throw new InternalAuthenticationServiceException(
                    repositoryProblem.getMessage(), repositoryProblem);
        }
​
        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException(
                    "UserDetailsService returned null, which is an interface contract violation");
        }
        return loadedUser;
    }

可以看到此处的返回对象 userDetails 是由 UserDetailsService 的 #loadUserByUsername(username) 来获取的。

 

对于看本文比较困难的同学可以移步:https://www.lanqiao.cn/courses/3013

邀请码:STyLDQzM

其实内容和博客差不多,只不过更详细(就是一步一步的来,所有步骤有详细过程记录和截图,按照课程步骤最终能完整的操作完整个项目过程,适合小白),版本上也有所升级,还有就是有问题可以直接沟通,谢谢支持!

### Information Security in IT Field Information security within the IT field encompasses practices designed to protect systems, networks, and programs from digital attacks. These cybersecurity measures are essential as they ensure confidentiality, integrity, and availability of information. In the context of rapidly evolving technology such as artificial intelligence (AI), achieving certification in related fields like AI can provide professionals with advanced skills that enhance their ability to implement robust security protocols[^1]. This includes developing intelligent algorithms capable of identifying potential threats or vulnerabilities before they become critical issues. Regarding specific challenges faced by modern information security efforts, one significant concern arises from advancements in data mining techniques which have led to increased risks associated with personal privacy breaches[^2]. As industries leverage big data analytics for various applications, there is a heightened need for stringent safeguards against unauthorized access to sensitive user information. For practical implementation at an organizational level using frameworks like Spring Security, developers must consider multiple aspects including but not limited to username/password authentication mechanisms, two-factor verification via SMS codes, session management features allowing forced logouts when necessary, custom authorization filters based on business logic requirements, URL pattern matching combined with expressions for fine-grained control over resource accessibility, secure logout procedures ensuring all tokens are invalidated upon termination of sessions, and scalable configurations supporting distributed environments where services may reside across different nodes or cloud platforms[^3]. ```java <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ```
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

盡盡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值