SpringSecurity Web权限方案

本文详细介绍了如何使用Spring Security实现Web权限管理,包括配置登录账号、查询数据库验证、自定义登录页面、403访问页面、登录成功跳转、登出功能、记住用户、基于角色和权限的访问控制,以及CSRF防护。内容涵盖从硬编码账号到数据库查询用户,再到前后端交互的安全设置,为Web应用提供了全面的安全基础配置。
摘要由CSDN通过智能技术生成

三、SpringSecurity Web 权限方案


1、设置登录账号、密码

  1. application.properties添加相应的配置

    • spring.security.user.name=test
      spring.security.user.password=test
      
  2. 编写配置类

    • @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
              BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
              String password = bCryptPasswordEncoder.encode("test");
              auth.inMemoryAuthentication().withUser("test").password(password).roles("admin");
          }
      
          @Bean
          PasswordEncoder password(){
              return new BCryptPasswordEncoder();
          }
      }
      
  3. 自定义实现类

    • //自定义实现UserDetailsService接口
      @Service
      public class MyUserDetailsService implements UserDetailsService {
      
          @Autowired
          private UsersMapper usersMapper;
      
          @Override
          public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
              //给用户赋值权限、或者角色,也可以从数据库查询
              List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("test");
              //返回的是SpringSecurity自带的UserDeatils接口实现类
              return new User("test","test",auths);
          }
      }
      
    • //编写配置类
      @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Autowired
          private UserDetailsService userDetailsService;
      
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
              auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
                  @Override
                  public String encode(CharSequence charSequence) {
                      return charSequence.toString();
                  }
      
                  @Override
                  public boolean matches(CharSequence charSequence, String s) {
                      return s.equals(charSequence.toString());
                  }
              });
          }
      }
      


2、查询数据库完成验证

上面都是写死了登录帐号、密码,接下来演示通过数据库查询,整合了MybatisPlus自行学习

  1. 编写自定义类实现UserDetailsService接口

    • //编写自定义类实现UserDetailsService接口
      @Service
      public class MyUserDetailsService implements UserDetailsService {
      
          @Autowired
          private UsersMapper usersMapper;
      
          @Override
          public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
              QueryWrapper<Users> wrapper = new QueryWrapper<>();
              wrapper.eq("username",s);
              Users users = usersMapper.selectOne(wrapper);
              //以上是MybatisPlus从数据库查询用户信息相关操作,自行学习
              if( users == null){
                  throw new UsernameNotFoundException("用户名不存在!");
              }
              //给用户赋值权限、或者角色,也可以从数据库查询
              List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("test");
              return new User(users.getUsername(), users.getPassword(), auths);
          }
      }
      
  2. 编写配置类

    • //编写配置类
      @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Autowired
          private UserDetailsService userDetailsService;
      
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
              auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
                  @Override
                  public String encode(CharSequence charSequence) {
                      return charSequence.toString();
                  }
      
                  @Override
                  public boolean matches(CharSequence charSequence, String s) {
                      return s.equals(charSequence.toString());
                  }
              });
          }
      }
      


3、自定义登录页面

  1. 前端页面代码

    • <!DOCTYPE html>
      <head lang="en">
          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
          <title>login页面</title>
      </head>
      <body>
      
      
      <h1>表单提交</h1>ht
      <form action="/user/login"  method="post">
          <input type="text" name="username" />
          <input type="text" name="password" />
          <input type="submit" />
      </form>
      </body>
      </html>
      
  2. 编写配置类

    • //编写配置类
      @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Autowired
          private UserDetailsService userDetailsService;
      
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.formLogin()
                      .loginPage("/login.html")//设置登录页面
                      .loginProcessingUrl("/user/login")//设置表单action提交路径
                      .defaultSuccessUrl("/test/index").permitAll();//登录成功后跳转的路径
      
              http.authorizeRequests()//对于路径请求
                      .antMatchers("/", "/test/hello", "/user/login").permitAll()//这类路径直接放行
                      .anyRequest().authenticated();//其他的任何请求都需要认证
      
              http.csrf().disable();//关系CSRF
          }
      
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
              auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
                  @Override
                  public String encode(CharSequence charSequence) {
                      return charSequence.toString();
                  }
      
                  @Override
                  public boolean matches(CharSequence charSequence, String s) {
                      return s.equals(charSequence.toString());
                  }
              });
          }   
      }
      


4、自定义403访问页面

  1. 前端页面

    • <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>没有权限访问页面</title>
      </head>
      <body>
          <h1>没有访问权限!</h1>
      </body>
      </html>
      
  2. 编写配置类

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //自定义403访问页面
            http.exceptionHandling().accessDeniedPage("/unauth.html");
    
            http.formLogin()
                    .loginPage("/login.html")
                    .loginProcessingUrl("/user/login")
                    .defaultSuccessUrl("/test/index").permitAll();
    
            http.authorizeRequests()
                    .antMatchers("/", "/user/login").permitAll()
                    .antMatchers("/test/hello").hasAnyRole("admin,test")
                    .anyRequest().authenticated();
    
            http.csrf().disable();
        }
    }
    


5、自定义登录成功跳转页面

  1. 前端页面

    • <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
          登录成功!
          <a href="/logout">退出</a>
      </body>
      </html>
      
  2. 编写配置类

    • @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Autowired
          private UserDetailsService userDetailsService;
      
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              //自定义403访问页面
              http.exceptionHandling().accessDeniedPage("/unauth.html");
      
              http.formLogin()
                      .loginPage("/login.html")
                      .loginProcessingUrl("/user/login")
                      .defaultSuccessUrl("/test/index").permitAll();//成功后跳转的路径
      
              http.authorizeRequests()
                      .antMatchers("/", "/user/login").permitAll()
                      .antMatchers("/test/hello").hasAnyRole("admin,test")
                      .anyRequest().authenticated();
      
              http.csrf().disable();
          }
      }
      


6、自定义登出功能

  1. ​ 编写配置类

    • @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Autowired
          private UserDetailsService userDetailsService;
      
          @Override
          protected void configure(HttpSecurity http) throws Exception {
          	http.logout().logoutUrl("/logout").//自定义登出路径
                      logoutSuccessUrl("/test/hello").permitAll();//登出后跳转到的页面
          
              //自定义403访问页面
              http.exceptionHandling().accessDeniedPage("/unauth.html");
      
              http.formLogin()
                      .loginPage("/login.html")
                      .loginProcessingUrl("/user/login")
                      .defaultSuccessUrl("/test/index").permitAll();//成功后跳转的路径
      
              http.authorizeRequests()
                      .antMatchers("/", "/user/login").permitAll()
                      .antMatchers("/test/hello").hasAnyRole("admin,test")
                      .anyRequest().authenticated();
      
              http.csrf().disable();
          }
      }
      


7、记住用户的功能实现

  1. 流程图:

    • 在这里插入图片描述
  2. 实现原理:

    • 在这里插入图片描述
  3. 实现流程

    1. 前端页面

      • <!DOCTYPE html>
        <head lang="en">
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
            <title>login页面</title>
        </head>
        <body>
        
        
        <h1>表单提交</h1>ht
        <form action="/user/login"  method="post">
            <input type="text" name="username" />
            <input type="text" name="password" />
            <input type="checkbox"name="remember-me"title="记住密码"/><br/>
            <input type="submit" />
        </form>
        </body>
        </html>
        
    2. 创建数据库表

      • CREATE TABLE `persistent_logins` (
         `username` varchar(64) NOT NULL,
         `series` varchar(64) NOT NULL,
         `token` varchar(64) NOT NULL,
         `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 
        CURRENT_TIMESTAMP,
         PRIMARY KEY (`series`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
        
    3. 配置类中要注入数据源,配置操作数据库对象

      • @Configuration
        @EnableWebSecurity
        public class SecurityConfig extends WebSecurityConfigurerAdapter {
        
            @Autowired
            private DataSource dataSource;
        
                @Bean
            public PersistentTokenRepository persistentTokenRepository(){
                JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
                jdbcTokenRepository.setDataSource(dataSource);
                return jdbcTokenRepository;
            }
        }
        
    4. 配置类中配置自动登录

      • @Configuration
        @EnableWebSecurity
        public class SecurityConfig extends WebSecurityConfigurerAdapter {
        
            @Autowired
            private DataSource dataSource;
        
            @Bean
            public PersistentTokenRepository persistentTokenRepository(){
                JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
                jdbcTokenRepository.setDataSource(dataSource);
                return jdbcTokenRepository;
            }
            
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http.logout().logoutUrl("/logout").
                        logoutSuccessUrl("/test/hello").permitAll();
        
                http.exceptionHandling().accessDeniedPage("/unauth.html");
        
                http.formLogin()
                        .loginPage("/login.html")
                        .loginProcessingUrl("/user/login")
                        .defaultSuccessUrl("/test/index").permitAll();
        
                http.authorizeRequests()
                        .antMatchers("/", "/user/login").permitAll()
                        .antMatchers("/test/hello").hasAnyRole("admin,test")
                        .anyRequest().authenticated();
        
                http.rememberMe().tokenRepository(persistentTokenRepository())//开启记住用户的选项
                        .tokenValiditySeconds(60)//设置有效时长,单位秒
                        .userDetailsService(userDetailsService);
        
                http.csrf().disable();
            }
        }
        


8、基于角色或权限进行访问控制

  1. hasAuthority 方法

    • 含义:如果当前的主体具有指定的权限,则返回 true,否则返回 false

    • .antMatchers("/test/hello").hasAuthority("admin")
      
    • 在这里插入图片描述

  2. hasAnyAuthority 方法

    • 含义:如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true

    • .antMatchers("/test/hello").hasAnyAuthority("admin,test")
      
  3. hasRole 方法

    • 含义:如果用户具备给定角色就允许访问,否则出现 403。如果当前主体具有指定的角色,则返回 true。

    • .antMatchers("/test/hello").hasRole("test")
      
    • 注意事项:在这里插入图片描述

  4. hasAnyRole

    • 含义:表示用户具备任何一个角色都可以访问。

    • .antMatchers("/test/hello").hasAnyRole("admin,test")
      


9、认证授权注解使用

  1. @Secured

    • 需要在配置类上先开启注解功能**@EnableGlobalMethodSecurity(securedEnabled=true)**

    • 含义:判断是否具有角色,另外需要注意的是**这里匹配的字符串需要添加前缀“ROLE_“**。

    • @RestController
      @RequestMapping("test")
      public class IndexController {
      
          @GetMapping("hello")
          @Secured({"ROLE_role"})
          public String hello() {
              return "hello security";
          }
      }
      
  2. @PreAuthorize

    • 需要在配置类上先开启注解功能**@EnableGlobalMethodSecurity(prePostEnabled = true)**

    • 含义:适合进入方法前的权限验证。判断是否有权限进入方法

    • @RestController
      @RequestMapping("test")
      public class IndexController {
      
          @GetMapping("hello")
          @PreAuthorize("hasAnyAuthority('admin,test')")
          public String hello() {
              return "hello security";
          }
      }
      
  3. @PostAuthorize

    • 需要在配置类上先开启注解功能**@EnableGlobalMethodSecurity(prePostEnabled = true)**

    • 含义:在方法执行后再进行权限验证。是否有权限返回。

    • @RestController
      @RequestMapping("test")
      public class IndexController {
      
          @GetMapping("hello")
          @PostAuthorize("hasAnyAuthority('admin,test')")
          public String hello() {
              return "hello security";
          }
      }
      
  4. @PreFilter

    • 含义:进入控制器之前对数据进行过滤。

    • @RestController
      @RequestMapping("test")
      public class IndexController {
      
          @GetMapping("hello")
          @PreFilter(value = "filterObject.id%2==0")//只允许id为偶数的进入方法
          public String hello() {
              return "hello security";
          }
      }
      
  5. @PostFilter

    • 含义:方法返回时对数据进行过滤。

    • @RestController
      @RequestMapping("test")
      public class IndexController {
      
          @GetMapping("hello")
          @PostFilter("filterObject.username == 'admin1'")//只留下用户名是 admin1 的数据
          public String hello() {
              return "hello security";
          }
      }
      


10、CSRF应用

  1. CSRF 理解

    • 跨站请求伪造也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF
    • 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
    • 跨站请求攻击,简单地说,是**攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)**。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。
    • 利用了 web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的
  2. Spring Security 实现 CSRF 的原理:

    1. 生成 csrfToken 保存到 HttpSession 或者 Cookie 中。
    2. 请求到来时,从请求中提取 csrfToken,和保存的 csrfToken 做比较,进而判断当前请求是否合法。主要通过 CsrfFilter 过滤器来完成。
  3. 实现过程:

    需要整合thymeleaf,自行学习

    1. 前端页面里面添加 <input type=“hidden” name="${_csrf.parameterName}" value=“​${_csrf.token}” />

      • <!DOCTYPE html>
        <html  xmlns:th="http://www.thymeleaf.org">
        <head lang="en">
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
            <title>xx</title>
        </head>
        <body>
        
        
        <h1>表单提交</h1>
        <!-- 表单提交用户信息,注意字段的设置,直接是*{} -->
        <form action="/user/login"  method="post">
            //需要添加下面这行存储csrftoken
            <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
            <input type="text" name="username" />
            <input type="text" name="password" />
            <input type="checkbox"name="remember-me"title="记住密码"/><br/>
            <input type="submit" />
        </form>
        </body>
        </html>
        
    2. 配置类中去掉 http.csrf().disable();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值