SpringSecurity入门
作者:mmseoamin日期:2023-12-25

前言

Spring Security是一个用于在Java应用程序中提供身份验证和授权功能的强大框架。它构建在Spring框架之上,为开发人员提供了一套灵活且全面的安全性服务,本篇将为大家带来Spring Security的详细介绍及入门

SpringSecurity入门,第1张

一.安全框架

在学习了解Spring Security之前先了解什么是安全框架。安全框架顾名思义,就是解决系统安全问题的框架。任何应用开发的计划阶段都应该确定一组特定的安全需求,如身份验证、授权和加密方式。不使用安全框架之前,我们需要手动处理每个资源的访问控制,针对不同的项目都需要做不同的处理,此时就会显得非常麻烦,并且低效率引起的额外开销会延缓开发周期。使用安全框架,使开发团队能够选择最适合这些需求的框架,可以通过配置的方式实现对资源的访问限制,使得开发更加的高效。

二.主流安全框架

1.Spring Security:

Spring Security是目前Java Web领域中最流行的框架之一,它提供了一系列安全级别,包括基于认证和授权的安全保护,以及各种各样的安全校验,使得开发人员可以非常容易地为应用程序添加安全保护。Spring Security也提供了一种简单易用的方式来定制其过滤器链,以适应具体的安全需求。

2.Apache Shiro:

Apache Shiro是一个易于使用的Java Web安全框架。它提供了一种简单的方式来管理应用程序中的身份验证、授权和加密。Apache Shiro可以轻松地集成到Spring中,并提供了一系列构建块,以便定制其安全性行为。

3.JSON Web Token (JWT):

(1)不是一个完整的安全框架,但是被广泛用于在客户端和服务器之间安全地传递信息。

(2)常用于身份验证和声明传递。

JWT在前面的文章有过详细介绍,这里就不过多详述

文章链接一篇文章让你了解“JWT“-CSDN博客

 三.为什么选择Spring Security

在介绍了这么多的主流安全框架,为什么要选择Spring Security呢?

SpringBoot 没有发布之前,Shiro 应用更加广泛,因为 Shiro 是一个强大且易用的 Java 安全框架,能够非常清晰的处理身份验证、授权、管理会话以及密码加密。利用其易于理解的API,可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。但是 Shiro 只是一个框架而已,其中的内容需要自己的去构建,前后是自己的,中间是Shiro帮我们去搭建和配置好的。

SpringBoot 发布后,随着其快速发展,Spring Security(前身叫做Acegi Security) 重新进入人们的视野。SpringBoot 解决了 Spring Security 各种复杂的配置,Spring Security 在我们进行用户认证以及授予权限的时候,通过各种各样的拦截器来控制权限的访问,从而实现安全,也就是说 Spring Security 除了不能脱离 Spring,Shiro 的功能它都有。

  • 在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。

  • 在用户授权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。

    而对于Shiro而言,Shiro在这个环境下实际已经不具备优势了。因为Spring这个生态链现在是太强大了。

    四.SpringSecurity讲解

    1.什么是SpringSecurity?

    Spring Security是一个基于Spring框架的安全性框架,可用于对Java应用程序进行身份验证、授权和其他安全性功能的添加。它不仅可以对Web应用程序进行保护,还可以保护非Web环境下的应用程序,如远程服务和命令行应用程序等。Spring Security提供了一系列可插拔的安全性特性,如基于标记的身份验证、权限控制、单点登录、密码加密等。它还支持多种安全性协议和标准,如OAuth、SAML、OpenID等,可与各种身份提供商集成。

    2.工作原理

    权限框架一般包含两大核心模块:认证(Authentication)和鉴权(Authorization)。

    • 认证:认证模块负责验证用户身份的合法性,生成认证令牌,并保存到服务端会话中(如TLS)。

    • 鉴权:鉴权模块负责从服务端会话内获取用户身份信息,与访问的资源进行权限比对。

      SpringSecurity入门,第2张

      核心组件介绍:

      • AuthenticationManager:管理身份验证,可以从多种身份验证方案中选择一种。

      • Authentication:用于验证用户的身份。

      • SecurityContextHolder:用于管理 SecurityContext 的 ThreadLocal,以便在整个请求上下文中进行访问,方便用户访问。

      • AccessDecisionManager:负责对访问受保护的资源的请求进行决策(即决定是否允许用户访问资源)

      • AccessDecisionVoter:是AccessDecisionManager的实现组件之一,它用于对用户请求的访问受保护的资源所需要的角色或权限进行投票。

      • ConfigAttribute:用于表示受保护资源或URL需要的访问权限,它可以理解为是访问控制策略的一部分

         工作步骤:

        1. 用户认证:当用户尝试访问受保护的资源时,Spring Security首先会进行用户认证。用户提供的用户名和密码将被验证,并与存储在数据库或其他身份验证来源中的用户凭据进行比较。

        2. 认证成功处理:如果用户的认证成功,Spring Security将生成一个安全上下文(SecurityContext),其中包含用户的身份信息和授权信息。这个上下文将被存储在安全上下文持有者(SecurityContextHolder)中,以供后续的请求使用。

        3. 授权处理:一旦用户通过认证,Spring Security将根据用户的角色和权限来判断用户是否有权访问请求的资源。它可以通过注解、配置文件或编程方式来配置授权规则。如果用户被授予访问资源的权限,Spring Security将允许用户继续请求。

        4. 认证失败处理:如果用户的认证失败,Spring Security将根据配置的处理方式返回响应,例如重定向到登录页面、返回错误信息等。

        5. 安全上下文传递:在整个请求处理过程中,Spring Security将安全上下文传递给其他组件,以确保每个组件都可以访问到用户的身份信息和授权信息。这样,在业务逻辑层、控制器层和视图层等各个组件中都可以使用安全上下文来进行身份验证和授权的处理。

        3.快速入门

        3.1 基于SpringBoot创建项目

        创建SpringBoot所需要导入的依赖

        
        
            org.springframework.boot
            spring-boot-starter-freemarker
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        配置application.yml 文件

        spring:
          freemarker:
            # 设置freemarker模板后缀
            suffix: .ftl
            # 设置freemarker模板前缀
            template-loader-path: classpath:/templates/
            enabled: true

        3.2 配置Spring Security

        导入Spring Security依赖

        
            org.springframework.boot
            spring-boot-starter-security
        

        依赖详解

        • spring-security-core:Spring Security的核心模块,提供了基于权限的访问控制以及其他安全相关功能。

        • spring-security-config:提供了Spring Security的配置实现,例如通过Java配置创建安全策略和配置Token存储等。

        • spring-security-web:提供了Spring Security Web的基本功能,例如Servlet集成和通过HttpSecurity配置应用程序安全策略。

        在application.yml文件中配置自定义用户名和密码

        spring:
          security:
            user:
              name: admin
              password: 123456

        4.Web安全配置类

        4.1HttpSecurity介绍

        HttpSecurity 是 Spring Security 的一个核心类,用于配置应用程序的安全策略。

        HttpSecurity 类通常包含许多方法,可以用于配置以下内容:

        1. HTTP 请求的安全策略,例如访问控制、跨站点请求伪造 (CSRF) 防护等。

        2. HTTP 验证的安全策略,例如基于表单、HTTP 基本身份验证、OAuth 等。

        3. 访问受保护资源时所需的身份验证和授权方式。

        方法说明
        authorizeRequests()用于配置如何处理请求的授权,默认情况下所有的请求都需要进行认证和授权才能访问受保护的资源
        formLogin()用于配置基于表单的身份验证,包括自定义登录页面、登录请求路径、用户名和密码的参数名称、登录成功和失败的跳转等。
        httpBasic()用于配置基于HTTP Basic身份验证,包括定义使用的用户名和密码、realm名称等。
        logout()用于配置退出登录功能,包括定义退出登录请求的URL、注销成功后的跳转URL、清除会话、删除Remember-Me令牌等。
        csrf()用于配置跨站请求伪造保护,包括定义CSRF Token的名称、保存方式、忽略某些请求等。
        sessionManagement()用于配置会话管理,包括定义并发控制、会话失效、禁用URL重定向、会话固定保护等。
        rememberMe()用于配置Remember-Me功能,包括定义Remember-Me令牌的名称、有效期、加密方法、登录成功后的处理方式等。
        exceptionHandling()用于配置自定义的异常处理,包括定义异常处理器和异常处理页面等。
        headers()用于配置HTTP响应头信息,包括定义X-Content-Type-Options、X-XSS-Protection、Strict-Transport-Security等头信息。
        cors()用于配置跨域资源共享,包括定义可访问的来源、Headers等。
        addFilter()用于向当前HttpSecurity中添加自定义的Filter。
        and()用于在配置中添加另一个安全规则,并将两个规则合并。

        匹配规则:

        • URL匹配

          方法说明
          requestMatchers()配置一个request Mather数组,参数为RequestMatcher对象,其match规则自定义,需要的时候放在最前面,对需要匹配的的规则进行自定义与过滤
          authorizeRequests()URL权限配置
          antMatchers()配置一个request Mather的string数组,参数为ant路径格式, 直接匹配url
          anyRequest()匹配任意url,无参 ,最好放在最后面
          • 保护URL

            方法说明
            authenticated()保护Url,需要用户登录
            permitAll()指定URL无需保护,一般应用与静态资源文件
            hasRole(String role)限制单个角色访问
            hasAnyRole(String… roles)允许多个角色访问
            access(String attribute)该方法使用 SPEL, 所以可以创建复杂的限制
            hasIpAddress(String ipaddressExpression)限制IP地址或子网
            • 登录formLogin

              方法说明
              loginPage()设置登录页面的 URL
              defaultSuccessUrl()设置登录成功后的默认跳转页面
              failuerHandler()登录失败之后的处理器
              successHandler()登录成功之后的处理器
              failuerUrl()登录失败之后系统转向的url,默认是this.loginPage + “?error”
              loginProcessingUrl()设置登录请求的 URL,即表单提交的 URL
              usernameParameter()设置登录表单中用户名字段的参数名,默认为 username
              passwordParameter()设置登录表单中密码字段的参数名,默认为 password
              • 登出logout

                方法说明
                logoutUrl()登出url , 默认是/logoutl
                logoutSuccessUrl()登出成功后跳转的 url 默认是/login?logout
                logoutSuccessHandler()登出成功处理器,设置后会把logoutSuccessUrl 置为null

                4.2 自定义登录

                1.创建SecurityConfig配置类

                创建WebSecurityConfig配置类,设置@EnableWebSecurity注解开启Spring Security的默认行为。

                package com.yu.security.config;
                import org.springframework.context.annotation.Bean;
                import org.springframework.context.annotation.Configuration;
                import org.springframework.security.config.annotation.web.builders.HttpSecurity;
                import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
                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.crypto.bcrypt.BCryptPasswordEncoder;
                import org.springframework.security.crypto.password.PasswordEncoder;
                import org.springframework.security.provisioning.InMemoryUserDetailsManager;
                import org.springframework.security.web.SecurityFilterChain;
                @Configuration //声明为配置类,与Spring绑定一起启动
                @EnableWebSecurity //开启SpringSecurity的默认行为
                public class WebSecurityConfig {
                    @Bean
                    public PasswordEncoder bcryptPasswordEncoder() {
                        return new BCryptPasswordEncoder();
                    }
                    @Bean
                    public UserDetailsService userDetailsService() {
                        UserDetails admin = User.withUsername("admin")
                                .password(bcryptPasswordEncoder().encode("123456"))
                                .roles("ADMIN", "USER").build();
                        UserDetails user = User.withUsername("user")
                                .password(bcryptPasswordEncoder().encode("123456"))
                                .roles("USER").build();
                        return new InMemoryUserDetailsManager(admin, user);
                    }
                    @Bean
                    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
                        http.authorizeRequests()
                                .antMatchers("/toLogin").permitAll()
                                .antMatchers("/admin/**").hasRole("ADMIN")
                                .antMatchers("/user/**").hasAnyRole("ADMIN", "USER")
                                .anyRequest().authenticated()
                                .and()
                                .formLogin()
                                .loginPage("/toLogin")
                                .loginProcessingUrl("/userLogin")
                                .usernameParameter("username")
                                .passwordParameter("password")
                                .and()
                                .logout()
                                .logoutUrl("/logout")
                                .logoutSuccessUrl("/");
                        return http.build();
                    }
                }
                2.创建Controller

                控制路由

                @Controller
                public class UserController {
                    @RequestMapping("/toLogin")
                    public String toLogin(){
                        return "login";
                    }
                    @RequestMapping("/userLogin")
                    public String userLogin(String username,String password){
                        System.out.println("username="+username+",password="+password);
                        return "index";
                    }
                    @RequestMapping("/admin/toAddUser")
                    public String toAddUser(){
                        return "admin/addUser";
                    }
                    @RequestMapping("/admin/toListUser")
                    public String toListUser(){
                        return "admin/listUser";
                    }
                    @RequestMapping("/admin/toResetPwd")
                    public String toResetPwd(){
                        return "admin/resetPwd";
                    }
                    @RequestMapping("/admin/toUpdateUser")
                    public String toUpdateUser(){
                        return "admin/updateUser";
                    }
                    @RequestMapping("/user/toUpdatePwd")
                    public String toUpdatePwd(){
                        return "user/updatePwd";
                    }
                }
                3.登录模板页面

                用户登录



                4.配置多角色访问

                在application.yml文件进行配置

                spring:
                  freemarker:
                    suffix: .ftl
                    template-loader-path: classpath:/templates/
                    enabled: true
                5.配置自定义异常处理器

                修改SecurityConfig配置类,添加自定义异常处理,并设置异常处理页面。

                http.exceptionHandling().accessDeniedPage("/noAccess");

                当我们做权限管理时,普通用户误操作其他管理员界面,会进行异常跳转页面处理,这个要根据实际情况实际考虑

                4.3异常小结

                重启项目,跳转登录页重新登录。这时发现登录不成功,后台控制台也没有产生任何异常信息,通过浏览器的网络(network)查看现实登录请求接口302错误。

                SpringSecurity入门,第3张

                解决方案:关闭csrf

                修改SecurityConfig配置类,添加关闭csrf配置。

                http.csrf().disable();

                 总结:

                Spring Security可以很好得保证我们的安全性问题,但是在本次的自定义登录中还是要注意:

                1.因为使用的是模拟登录,所以在实际操作创建用户时还是要按正规程序执行,并将限制用户进行传参验证

                2.在Controller层中配置创建路由时,要注意路由名称,配置模板信息要与yml配置文件一致