springboot整合springsercurity

本文深入探讨了Spring Security在Web应用安全中的作用,包括用户认证和授权。介绍了如何配置Spring Security,使用默认用户名和密码,自定义登录页面,以及通过配置类实现用户认证。同时,讲解了授权机制,如使用注解进行权限控制,并展示了注销和"记住我"功能的实现。此外,还讨论了Spring Security的异常处理和登录失败后的处理策略。
摘要由CSDN通过智能技术生成

(一) 修改用户名密码

简介

Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。

代码如下:

  1. 导入对应坐标,web+security
<!--    web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

<!--    spring安全控制    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

2.1 默认用户名: user,启动工程,控制台会输出,security随机生成的密码 如图:
提示密码

2.2 访问项目根路径 localhost:8080,发现跳到了登录页
登录

  1. 基本原理分析:
    • SpringSecurity本质是一个过滤器链
      • FilterSecurityInterceptor :权限过滤器,基本位于过滤链的最底部
      • ExceptionTranslationFilter :异常过滤器,处理认证授权过程中抛出的异常
      • UsernamePasswordAuthenticationFilter :对/login的POST请求做拦截,校验表单中用户名,密码。
    • 两个重要接口
      • UserDetailsService:进行查询数据库账号密码的过程
        • 创建类继承 UsernamePasswordAuthenticationFilter,重写三个方法
        • 创建UserDetailsService实现类,编写查询数据库过程,返回User对象,这个User对象是security框架提供的
      • PasswordEncoder:数据加密接口(加密密码)
  2. 设置登录的用户名和密码:三种方式
    1. 配置文件 applicaiton.yml:
      	spring:
      	  security:
      	    user:
      	      name: admin
      	      password: 123123
      	```
      
    2. 配置类:编写配置类,继承WebSecurityConfigurerAdapter
      @Configuration
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Override//认证
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      //        密码加密器,它实现了 PasswordEncoder 接口
              BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
              String password = passwordEncoder.encode("zhangsan");
      
              auth.inMemoryAuthentication() //内存中进行校验
                      .withUser("zhangsan").password(password).roles("vip1");
          }
      
          @Bean //由于加密时需要用到 PasswordEncoder接口,所以要放到容器中,否则报错
          public PasswordEncoder passwordEncoder(){
              return new BCryptPasswordEncoder();
          }
      }
      
    3. 自定义编写实现类 (重点、常用)
      • 配置类:
        @Configuration
        public class SecurityConfig extends WebSecurityConfigurerAdapter {
        
            @Autowired //注入服务层,
            UserDetailsService userDetailsService;
        
            @Override//身份验证管理器生成器
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
            }
        
            @Bean //由于加密时需要用到 PasswordEncoder接口,所以要放到容器中,否则报错
            public PasswordEncoder passwordEncoder() {
                return new BCryptPasswordEncoder();
            }
        }
        
      • 服务层:这里应该查询数据库,因为麻烦所以不查了
        @Service("userDetailsService")
        public class MyUserDetailsService implements UserDetailsService {
            @Override
            public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
                // 1. 查看源码发现 Security的User类 需要传入用户名  密码  Collection 集合
                // 2. 调用工具类中commaSeparatedStringToAuthorityList()方法创建权限集合 
                List<GrantedAuthority> list = AuthorityUtils.commaSeparatedStringToAuthorityList("vip");
                return new User("student",new BCryptPasswordEncoder().encode("student"),list);
            }
        }
        

Security的异常

  • DaoAuthenticationProvider中存在UsernameNotFoundException 等异常,查询数据库如果User为空,可以抛出(根具需求)

(2) 授权及自定义登录页、无权限访问页

代码如下

  1. 配置类中 重写父类的 configure(HttpSecurity http) 方法
    		//    授权
        /**
         * login.html中form表单提交地址为  /user/login
         * .formLogin()
         *      .loginPage("/login.html") 更改登录页面为自己编写的登录页
         *      .loginProcessingUrl("/user/login")登录访问路径
         *      .defaultSuccessUrl("/test/index").permitAll() 登录成功后,跳转路径
         *      
         * 
         * .authorizeRequests().antMatchers( //授权:哪些路径需要什么角色访问
         *      1. hasAuthority():当前用户具有指定的权限,返回true,否则 fals
         *      2. hasAnyAuthority():当前用户具有其中一个权限, 逗号分
         *      3. hasRole() 方法  可查看源码,创建对象权限时,权限前缀为:ROLE_xx
         *      4. hasAnyRole:同 3,逗号分隔
         *      5. anyRequest().authenticated()//所有请求都被访问
         *      6. csrf().disable();//关闭csrf防护
         */
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()                //登录页面设置
                    .loginPage("/login.html")
    //                .loginProcessingUrl("/user/login")
                    .defaultSuccessUrl("/test/index").permitAll()
                    .and().authorizeRequests()  
                        .antMatchers("/","/user/login").permitAll()
                        .antMatchers("/vip1/").hasRole("vip1")
    //                  .antMatchers("/test/index").hasAuthority("admins")
    //                  .antMatchers("/test/index").hasAnyAuthority("admins,student")
    //                  .antMatchers("/test/index").hasRole("admins")
    //                    .antMatchers("/test/index").hasAnyRole("admins","sale")
                    .anyRequest().authenticated()
                    .and().csrf().disable();
    				
    				// static目录下创建html页面   指定无权限访问页
                    http.exceptionHandling().accessDeniedPage("/denied.html");
    
        }
    
    1. 测试省略…

(3)认证授权注解使用

授权:
  1. 配置类主程序类添加注解 @EnableGlobalMethodSecurity() 详细见注释

    /**
     * 认证授权注解使用
     * 步骤:
     *      1. 启动类(配置类) 开启注解(需要哪个注解就开启哪个注解)@EnableGlobalMethodSecurity()
     *      2. 在controller的方法上面使用注解,设置角色
     *  注解:
     *      1. @Secured:用户具有某个角色可以访问方法
     *          * 需要开启 securedEnabled = true,
     *      2. @PreAuthorize:注解适合进入方法前的权限验证,可以将登录用户的 roles/permissions参数传到方法中
     *          * 需要开启 prePostEnabled = true
     *          * @PreAuthorize("hasAnyRole('ROLE_admins','ROLE_sale')")
     *      3. @PostAuthorize("hasAnyAuthority('admins')"):执行方法后,返回试图前 进行判断
     *          * 适合验证带有返回值的权限
     */
    @Controller
    public class SecurityTestController {
    
        //    @Secured({"ROLE_sale","ROLE_admin1"})
    //    @PreAuthorize("hasAnyAuthority('admins')")
    //    @PostAuthorize("hasAnyAuthority('admins')")
        @ResponseBody
        @RequestMapping("/update")
        @PostAuthorize("hasAnyAuthority('admins')")
        public String update() {
            System.out.println("test running...");
            return "hello update";
        }
    }
    
    • 前三个权限注解测试省略,
  2. 存在两个拦截器注解

    • @PostFilter:对方法返回数据进行过滤

      /*
      测试:
      	1. 添加@PostFilter注解   -->  filterObject为内置对象
      	2. list集合内传入两条数据
      	
      */
      @ResponseBody
      @RequestMapping("/update")
          @PreAuthorize("hasAnyAuthority('student')")
          @PostFilter("filterObject.username == '张三'")  // 若用户名为 张三 则放行
          public List<Users> update() {
              List<Users> list = new ArrayList<>();
              
              list.add(new Users(1,"张三","123"));
              list.add(new Users(2,"李四","123"));
              System.out.println(list);
      
              return list;
          }
      
      • 控制台输出控制台输出
      • 访问页面:
        测试
      • 测试成功,说明在执行完方法后,返回视图前,该注解会拦截掉不匹配的数据
    • @PreFilter: 对传入方法数据进行过滤

      • 该测试方法同上,请自行测试,

(4)注销、记住我功能

  • 注销功能: 在配置类中protected void configure(HttpSecurity http) 方法配置

    1. http.logout().logoutUrl("/logout").logoutSuccessUrl("/");
    2. 退出时进行的其它功能,查看LogoutConfigurer.class
      退出功能源码
  • 记住我功能

  1. 实现原理步骤1
    在这里插入图片描述

  2. 具体实现步骤

    1. 创建数据库表 persistent_logins 查看JdbcTokenRepositoryImpl源码发现也可以不创建,底层自动创建(配置即可)JdbcTokenRepositoryImpl
    2. 由于涉及数据库,所以需要导入mybatis或其它,配置文件完成数据库配置(此步骤省略)
    3. 配置类注入数据源,配置操作数据库对象
       @Autowired
      private DataSource dataSource;
      
      @Bean
      public PersistentTokenRepository persistentTokenRepository(){ //操作数据库对象,
          JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
          jdbcTokenRepository.setDataSource(dataSource);//设置数据源,因为需要操作数据库
          jdbcTokenRepository.setCreateTableOnStartup(true); //启动时自动创建表,如果存在该表则报错
          return jdbcTokenRepository;
      }
      
    4. 配置类中 protected void **configure(HttpSecurity http)**方法设置自动登录配置
      //        自动登录
      http.rememberMe().tokenRepository(persistentTokenRepository()) //注入上面的数据库操作对象
              .tokenValiditySeconds(60)  //设置有效时长  (秒)
              .userDetailsService(userDetailsService);  //注入userDetailsService,用于获取登录的用户
      
    5. 登录页面添加复选框,查看源码发现 name 必须为 remember-me,源码
      在这里插入图片描述
  3. 启动项目进行测试:选择记住我登录成功后,查看数据库,发现成功自动创建表,并存在数据数据库查看
    关闭浏览器,重新访问其它页面,无需登录成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值