• 【Spring Boot 集成应用】Spring Security集成整合配置使用


    1. Spring Security 简介

    Spring Security 是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。原名Acegi Security,最早于2003年起源于Spring社区。 2007年末正式归为Spring Framework的正式子项目,并改名为Spring Security 。此后, Spring Security有了长远发展,现已成为一款基于Spring Framework的广泛应用的安全框架,主要为应用服务提供用户认证(Authentication)和用户授权(Authorization)功能。详情可参考Spring Security 官方文档。

    Spring Security 针对安全方面的两大难题, 鉴权(Authentication)和授权(Authorization)提供了灵活强大的解决方案。

    • 用户鉴权(Authentication), 是指对用户身份的鉴权, 验证某个用户是否为系统中的合法对象, 是否能够访问对应的系统资源。比如用户输入账户和密码登陆系统。
    • 用户授权(Authorization),是指授予通过认证的用户指定的系统资源操作权限, 能否执行具体某个操作。比如用户能够访问操作的菜单,能够请求的功能接口, 这些都是系统资源。

    Spring Security优势:

    • 灵活性, Spring Security并不局限于Spring MVC,虽然它是基于Spring Framework实现的,但它并不依赖于Spring MVC,可以独立于MVC应用在其他Java EE框架之上。

    • 功能强大,Spring Security的安全管制并不只限制于Web请求,除此之外它还可以针对方法调用通过AOP的方式进行安全管制,甚至可以对域对象实例(Domain Object Instance)进行访问控制

    • 安全保护, 防止伪造身份, Spring Security 会自动拦截站点所有状态变化的请求(非GET,HEAD,OPTIONS和TRACE的请求),防止跨站请求伪造(CSRF防护),即防止其他网站或是程序POST等请求本站点。

    如果项目需要安全控制功能,不用自己去实现一套, 集成Spring Security专业安全框架是首选, 适用后台管理、接口资源管理、微服务统一鉴权等场景。

    2. Spring Security设计处理机制

    处理流程图:

    在这里插入图片描述

    • 客户端发起一个请求,进入 Security 过滤器链。

    • 当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到 logoutSuccessHandler 登出成功处理,如果登出失败则由 ExceptionTranslationFilter ;如果不是登出路径则直接进入下一个过滤器。

    • 当到 UsernamePasswordAuthenticationFilter 的时候判断是否为登录路径,如果是,则进入该过滤器进行登录操作,如果登录失败则到 AuthenticationFailureHandler 登录失败处理器处理,如果登录成功则到 AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器。

    • 当到 FilterSecurityInterceptor 的时候会拿到 uri ,根据 uri 去找对应的鉴权管理器,鉴权管理器做鉴权工作,鉴权成功则到 Controller 层否则到 AccessDeniedHandler 鉴权失败处理器处理。

    • 投票机制, 三种表决方式, 默认采用一票制(AffirmativeBased

      类名描述
      AffirmativeBased只要有一个投票器允许访问, 请求立刻允许放行, 不管之前存在拒绝的决定
      ConsensusBased多数票机制(允许或拒绝),多数的一方决定了AccessDecisionManager的结果。平局的投票和空票(全部弃权)的结果是可配置的
      UnanimousBased所有的投票器必须全部是允许的, 否则访问将被拒绝
    3. Spring Boot 与Spring Security 集成配置

    Spring Boot 与Spring Security 集成, 包含一般集成用法, 还包括自定义用户登陆页面使用, 自定义内存模式验证, 以及自定义登陆成功与失败逻辑处理。

    1. 创建工程spring-boot-security-integrate
      在这里插入图片描述

      启动类:

      com.mirson.spring.boot.security.integrate.startup.SecurityIntegrateApplication

      @SpringBootApplication
      @ComponentScan(basePackages = {"com.mirson"})
      public class SecurityIntegrateApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(SecurityIntegrateApplication.class, args);
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    2. MAVEN依赖

      <dependencies>
              
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-securityartifactId>
              dependency>
              
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-thymeleafartifactId>
              dependency>
              
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-webartifactId>
              dependency>
              
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-freemarkerartifactId>
              dependency>
          dependencies>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
    3. 定义一个外部访问接口

      • 创建实体

        定义一个用户实体

        com.mirson.spring.boot.security.integrate.po.User

        @Data
        public class User {
        
            /**
             * ID
             */
            private Integer id;
        
            /**
             * 用户名称
             */
            private String name;
        
            /**
             * 年龄
             */
            private String age;
        
            /**
             * 省份
             */
            private String province;
        
        
            /**
             * 创建时间
             */
            private Date createDate;
        
        }
        
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
      • 提供Web访问接口

        提供一个获取用户信息接口

        com.mirson.spring.boot.security.integrate.controller.UserController

        @RestController
        @RequestMapping("/user")
        @Log4j2
        public class UserController {
        
            @GetMapping("/getUserInfo")
            @ResponseBody
            public User getUserInfo() {
                User user = new User();
                user.setId(0);
                user.setAge("21");
                user.setName("user1");
                user.setCreateDate(new Date());
                return user;
            }
        
        }
        
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
    4. 工程配置

      server:
        port: 22618
      spring:
        application:
          name: security-integrate
      
        # security 安全配置
        security:
          user:
            name: "admin"
            password: "admin"
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      设置默认的用户名与密码为admin。

    5. 功能验证

      • 请求获取用户信息接口

        访问接口: http://127.0.0.1:22618/getUserInfo

        没有鉴权的情况下, 会出现登陆界面。

      在这里插入图片描述

      • 输入用户信息admin/admin, 再次请求获取用户信息接口

        在这里插入图片描述

        正确输入用户信息后, 可以正常访问用户信息接口。

    4. Spring Security 自定义鉴权实现
    4.1 自定义登陆页面处理

    Spring Security 内置会有一套登陆页面, 也可以自定修改, 这里通过freemark模板来实现自定登陆页面渲染。

    application.yml增加配置:

    # freemarker 模板配置
      freemarker:
        allow-request-override: false
        allow-session-override: false
        cache: true
        charset: UTF-8
        check-template-location: true
        content-type: text/html
        enabled: true
        expose-request-attributes: false
        expose-session-attributes: false
        expose-spring-macro-helpers: true
        prefer-file-system-access: true
        suffix: .ftl
        template-loader-path: classpath:/templates/
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    增加freemark模板文件:

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="">
        <meta name="author" content="">
    
        <title>自定义系统登陆title>
    
        <link href="/css/bootstrap.min.css" rel="stylesheet">
        <link href="/css/signin.css" rel="stylesheet">
      head>
    
      <body>
        <div class="container form-margin-top">
          <form class="form-signin" action="/user/doUserLogin" method="post">
            <h2 class="form-signin-heading" align="center">自定义系统登陆h2>
            <input type="text" name="username" class="form-control form-margin-top" placeholder="账号" required autofocus>
            <input type="password" name="password" class="form-control" placeholder="密码" required>
            <button class="btn btn-lg btn-primary btn-block" type="submit">sign inbutton>
          form>
        div>
        <footer>
          <p>support by: mirsonp>
        footer>
      body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    添加CSS静态资源文件

    在这里插入图片描述

    添加JAVA CONFIG配置:

    com.mirson.spring.boot.security.integrate.config.SpringSecurityConfiguration

    @Configuration
    @EnableWebSecurity
    public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
           http
                   .authorizeRequests()
                   .antMatchers("/user/userLoginForm",
                           "/css/**").permitAll()
                   .antMatchers("/user/getUserInfo").authenticated() 
                   .and()
                   .formLogin()
                   .loginPage("/user/userLoginForm")    //自定义登录页面             
                   .permitAll()            //允许所有人访问该路由
                   .and()
                   .csrf()
                   .disable()                //暂时禁用csrc否则无法提交
                   .httpBasic();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    重启, 访问接口: http://127.0.0.1:22618/user/getUserInfo

    会自动跳转到自定义的登陆页面:

    在这里插入图片描述

    4.2 自定义资源访问配置

    修改com.mirson.spring.boot.security.integrate.config.SpringSecurityConfiguration配置:

    @Configuration
    @EnableWebSecurity
    public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
           http
                   .authorizeRequests()
                   .antMatchers("/user/doUserLogin", "/user/userLoginForm",
                           "/css/**").permitAll()
                   .antMatchers("/user/getUserInfo").authenticated() //hasRole("ADMIN")
                   .and()
                   .formLogin()
                   .loginPage("/user/userLoginForm")    //自定义登录页面
                   .loginProcessingUrl("/user/doUserLogin") // 自定义登陆处理地址
                   .permitAll()            //允许所有人访问该路由
                   .and()
                   .csrf()
                   .disable()                //暂时禁用csrc否则无法提交
                   .httpBasic();
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    通过Spring Security 可以控制, 哪些资源需要受权限保护, 哪些可以开放访问。

    • 开放访问

      /user/userLoginForm 用户登陆页面

      /user/doUserLogin 登陆处理地址

      /css/** 静态资源文件

    • 权限保护

      /user/getUserInfo 获取用户信息接口

      可以指定Role角色权限, 不指定, 只要登陆即拥有访问权限。

    loginPage指定/user/userLoginForm为自定义登陆页面;

    loginProcessingUrl为登陆请求处理接口地址,可以不用对其做具体实现,Spring Security 会做默认处理。

    4.3 自定义内存模式鉴权
    1. 创建鉴权用户对象:

      com.mirson.spring.boot.security.integrate.po.OAuthUser

      @Data
      public class OAuthUser extends User {
      
          public OAuthUser(String account, String password){
      
              super(account, password, true, true, true, true, Collections.EMPTY_SET);
          }
      
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      继承的是Spring Security 的User对象, 将账号和密码信息, 传递至父类构造方法。

    2. 修改SpringSecurityConfiguration配置

      增加:

      @Configuration
      @EnableWebSecurity
      public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
          
          /**
           * 用户认证服务
           * */
          @Bean
          @Override
          protected UserDetailsService userDetailsService() {
              //创建基于内存用户管理对象
              InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
              //自定义权限
              Collection<GrantedAuthority> adminAuth = new ArrayList<>();
              adminAuth.add(new SimpleGrantedAuthority("ADMIN"));
              //自定义用户
              OAuthUser oAuthUser = new OAuthUser("admin", "admin123");
              manager.createUser(oAuthUser);
              return manager;
          }
          
          /**
           * 配置密码编码器, 不需加密
           * @return
           */
          @Bean
          public static NoOpPasswordEncoder passwordEncoder() {
              return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
          }
          
          ...
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33

      采用内存模式管理用户对象InMemoryUserDetailsManager, 自定义认证用户名和密码, 分别为admin,admin123。 需要配置密码编码器, 可以支持自定义密码加密方式, 这里不需加密, 配置NoOpPasswordEncoder。

    4.4 自定义登陆成功处理器

    Spring Security 提供了接口, 登陆成功, 可以通过处理器实现自定义逻辑。
    新建com.mirson.spring.boot.security.integrate.handler.SecuritySuccessHandler

    @Component
    @Log4j2
    public class SecuritySuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    
        @Autowired
        private ObjectMapper objectMapper;
    
        /**
         * 认证成功处理
         * @param request
         * @param response
         * @param authentication
         * @throws IOException
         * @throws ServletException
         */
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request,
                                            HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    
            log.info("Process in SecuritySuccessHandler ==> login success.");
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这里通过自定义登陆成功处理器, 将登陆成功的信息返回客户端。

    将处理器加入到自定义配置SpringSecurityConfiguration中:

    	@Override
        protected void configure(HttpSecurity http) throws Exception {
           http
                   .authorizeRequests()
                   .antMatchers("/user/doUserLogin", "/user/userLoginForm",
                           "/css/**").permitAll()
                   .antMatchers("/user/getUserInfo").authenticated() //hasRole("ADMIN")
                   .and()
                   .formLogin()
                   .loginPage("/user/userLoginForm")    //自定义登录页面
                   .loginProcessingUrl("/user/doUserLogin") // 自定义登陆处理地址
                   .successHandler(securitySuccessHandler)  // 自定义登陆成功处理器
                   .permitAll()            //允许所有人访问该路由
                   .and()
                   .csrf()
                   .disable()                //暂时禁用csrc否则无法提交
                   .httpBasic();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    4.5 自定义登陆失败处理器

    如果登陆失败, 也可以通过处理器实现自定义逻辑。
    新建com.mirson.spring.boot.security.integrate.handler.SecurityFailureHandler

    @Component
    @Log4j2
    public class SecurityFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                            AuthenticationException exception) throws IOException, ServletException {
    
            log.info("Process in SecurityFailureHandler ==> login failure.");
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(exception.getMessage()));
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    将登陆失败的错误信息, 返回给客户端。

    修改自定义配置SpringSecurityConfiguration:

    @Override
        protected void configure(HttpSecurity http) throws Exception {
           http
                   .authorizeRequests()
                   .antMatchers("/user/doUserLogin", "/user/userLoginForm",
                           "/css/**").permitAll()
                   .antMatchers("/user/getUserInfo").authenticated() //hasRole("ADMIN")
                   .and()
                   .formLogin()
                   .loginPage("/user/userLoginForm")    //自定义登录页面
                   .loginProcessingUrl("/user/doUserLogin") // 自定义登陆处理地址
                   .successHandler(securitySuccessHandler)  // 自定义登陆成功处理器
                   .failureHandler(securityFailureHandler)  // 自定义登陆失败处理器
                   .permitAll()            //允许所有人访问该路由
                   .and()
                   .csrf()
                   .disable()                //暂时禁用csrc否则无法提交
                   .httpBasic();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    4.6 自定义鉴权功能验证
    1. 验证内存模式鉴权

      内存模式我们设置的用户名和密码为admin/admin123

      默认配置文件是admin/admin

      在这里插入图片描述

      输入admin/admin123, 成功登陆, 内存模式已生效。

    2. 登陆成功处理器验证

      重启服务, 访问获取用户信息接口, http://127.0.0.1:22618/user/getUserInfo

      默认是会跳转到登陆页面, 如果没配置登陆成功处理器, 登陆成功后, 会进入上一次访问页面

      在这里插入图片描述

      可以看到, 登陆成功后, 并没有跳转到上一次访问的用户信息接口, 而是返回了登陆成功处理器的结果。

    3. 登陆失败处理器验证

      同样, 问获取用户信息接口, http://127.0.0.1:22618/user/getUserInfo, 会自动跳转到登陆页面。

      在这里插入图片描述

      采用错误的用户密码, 返回了登陆失败处理器的结果。

    5. 总结

    Spring Security 提供了鉴权与授权的功能支持, 这里做了详细讲解, 如何使用与配置, 并讲解了自定义鉴权处理功能, 实际业务当中,并非一层不变, 会做不同配置修改,比如自定义资源访问配置, 不同项目有不同的要求, 掌握这些自定义配置, 基本可以覆盖主要的业务场景, 针对更复杂的鉴权, 可以采用oauth2做鉴权处理, 在后续教程中会做讲解。

    教程源码下载地址: https://download.csdn.net/download/hxx688/86400104

  • 相关阅读:
    Mathorcup数学建模竞赛第四届-【妈妈杯】C题:面向多层次需求的西安旅游线路优化设计(lingo代码实现)
    输出1234无重复的三位数
    git常用命令
    计算机视觉-数学基础
    无视Win11 TPM/英特尔芯片等配置,强制升级Win11
    HDLbits: Lemmings2
    3、Sentinel 动态限流规则
    【云原生--Kubernetes】kubectl命令详解
    k8s--基础--10.2--命令--kubectl--常用命令
    微信小程序|自定义弹窗组件
  • 原文地址:https://blog.csdn.net/hxx688/article/details/126914012