(SpringBoot)Spring Security权限控制

简介

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的实际标准。

Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring Security的真正强大之处在于可以轻松扩展以满足自定义要求

特征:

  • 全面和可扩展的身份验证和授权支持
  • 防御会话固定,点击劫持,跨站点请求伪造等攻击
  • Servlet API集成
  • 与Spring Web MVC的可选集成

Spring Security提供了基于角色的访问控制访问控制列表(Access Controller List),可以对应用中领域对象进行细粒度的控制。

实战测试

1、实验环境搭建

  1. 新建一个初始的SpringBoot项目web模块,thymeleaf模块

  2. 导入静态资源

    • welcome.html
    • views
      • level1
        • 1.html
        • 2.html
        • 3.html
      • level2
        • 1.html
        • 2.html
        • 3.html
      • level3
        • 1.html
        • 2.html
        • 3.html
      • Login.html
  3. controller跳转

    @Controller
    public class RouterController{
      
      @RequestMapping({"/","index"})
      public String index(){
        return "index";
      }
      
      @RequestMapping("/toLogin")
      public String toLogin(){
        return "views/login";
      }
      
      @RequestMapping("/level/{id}")
      public String level1(@pathVariable("id") int id){
        return "views/level1/"+id;
      }
      
      @RequestMapping("/level2/{id}")
      public String level2(@pathVariable("id") int id){
        return "views/level2/"+id;
      }
      
      @RequestMapping("/level3/{id}")
      public String level3(@pathVariable("id") int id){
        return "views/level3/"+id;
      }
      
    }
    
  4. 测试实验环境是否正常

2、认识SpringSecurity

Spring Security是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,它可以实现强大的web安全控制,对于安全控制,我们仅需要引入spring-boot-starter-security模块,进行少量的配置,即可实现强大的安全管理!

记住几个类:

  • WebSecurityConfiguterAdapter: 自定义Security策略
  • AuthenticationManagerBuilder: 自定义认证策略
  • @EnableWebSecurity: 开启WebSecurity模式

”认证“(Authentication)

  • 身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份
  • 身份验证通常通过用户名和密码完成,有时候与身份验证因素结合使用

”授权“(Authorization)

  • 授权发生在系统成功验证您的身份之后,最终授予您访问资源(如:信息,文件,数据库,位置等几乎任何内容)的权限
  • 这个概念是通用的,而不是只在Spring Security中存在

3、认证和授权

使用Spring Security增加上认证和授权的功能

  1. 引入Spring Security模块

     <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
  2. 编写Spring Security配置类

    参考官网:https://spring.io/projects/spring-security

    帮助文档:https://docs.spring.io/spring-security/site/docs/5.4.2/reference/html5/

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
              .apply(customDsl())
              .flag(true)
              .and()
              ...;
        }
    
    }
    
  3. 编写基础配置类

    @Configuration
    @EnableWebSecurity //开启WebSecurity模式
    public class SecurityConfig extends webSecurityConfigurerAdapter{
      @Override
      protected void configure(HttpSecurity http) throws Exception{
        
      }
    }
    
  4. 定制请求的授权规则

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http
           .authorizeRequests()
           .antMatchers("/") //路径
           .permitAll() // 所有用户
           .antMatchers("/level1/**").hasRole("vip1") //vip1角色权限
           .antMatchers("/level2/**").hasRole("vip2") //vip2角色权限
           .antMatchers("/level3/**").hasRole("vip3"); //vip3角色权限
    }
    
  5. 测试登录

    ps:这里登录的用户必须有角色才行

  6. 在==configure()==方法中加入下面配置,开启自动配置登录功能

    //开启自动配置登录功能
    //login 请求来到登录页
    //login?error 重定向到这里表示登录失败
    http.formlogin();
    
    //目前的登录页 是 Spring Security 中 默认的首页
    
  7. 运行结果,如果没有权限,会跳转到登录页面
    在这里插入图片描述

  8. 定义认证规则,给某些用户添加角色,重写configure(AuthenticationManagerBuilder auth)

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()//在内存中定义,也可以选择jdbc中获取
                .withUser("zhangsan").password("123").roles("vip2","vip3")
                .and()
                .withUser("lisi").password("123").roles("vip1")
                .and()
                .withUser("wangwu").password("123").roles("vip1","vip2","vip3");
    }
    
  9. 测试,使用有角色的账号进行登录,发现报错,原因前端传过来的密码必须先加密才能登录
    在这里插入图片描述

  10. 给密码加密

    @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                    .inMemoryAuthentication()//在内存中定义,也可以选择jdbc中获取
                    .withUser("zhangsan").password(new BCryptPasswordEncoder().encode("123")).roles("vip2","vip3")
                    .and()
                    .withUser("lisi").password(new BCryptPasswordEncoder().encode("123")).roles("vip1")
                    .and()
                    .withUser("wangwu").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2","vip3");
        }
    
  11. 再次登录测试,登录成功,且每个角色只能访问自己认证允许访问的页面。

4、权限控制和注销

  1. 开启自动配置和注销功能

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	//注销请求, 也就是登出
      http.logout();
    }
    
  2. 在前端测试页面增加一个注销按钮

    <a class="item" th:href="@{/logout}">
      <i class="address card icon"></i>注销
    </a>
    
  3. 重启,测试注销,发现注销完毕后跳转的页面是 Spring Security默认登录页面,如果想要指定登出后的页面怎么办?

  4. 于是,需要增加一步处理

    //增加注销成功跳转页面 logoutSuccessUrl("/")
    http.logout().logoutSuccessUrl("/");
    
  5. 再次测试,发现已经可以跳转到我们指定页面

  6. 新需求:前端如果根据是否登录以及角色的权限显示不同内容

    我们需要结合thymeleaf中的一些功能

    sec:authorize=“isAuthenticated()”:是否认证登录! 来显示不同的页面

    • Maven依赖:

      <dependency> 
        <groupId>org.thymeleaf.extras</groupId> 
        <artifactId>thymeleaf-extras-springsecurity5</artifactId> 	 
        <version>3.0.4.RELEASE</version> 
      </dependency>
      
    • 前端页面

      • 导入命名空间

        xmlns:sec=“http://www.thymeleaf.org/thymeleaf-extras-springsecurity5”

      • 组件添加 sec:authorize 语句

        ​ 判断是否登录: sec:authorize="!isAuthenticated()"

        ​ 登录用户名:sec:authentication=“principal.username”

        ​ 登录用户角色: sec:authentication=“principal.authorities”

  7. 重启测试,页面显示,是我们需要已经进行判断后的页面

  8. ps:如果注销出现404,那是因为它默认防止csrf(跨站请求伪造),解决方式有两种:

    • 使用post请求

    • Spring Security中关闭csrf功能

      http.csrf().disable();
      

5、记住我 Remember Me)

记住我功能是绝大数网站都有提供的功能,目的:方便用户下次进入的时候,不用再次登录

如何实现记住我功能?

  1. 开启记住我功能

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	 http.rememberMe();    
    }
    
  2. 测试,发现Spring Security 的登录页面出现的 记住我按钮

  3. 登录,关闭浏览器,再次登录,发现用户依旧存在,实现了记住我功能

  4. 原理:

    • 当选中记住我的时候,登录完,可以查看浏览器的Coookies,发现了remember-me的cookie
    • 当点击注销的时候,remember-me 会从浏览器的Cookies中删除

6、首页定制

目前登录页面是Spring Security默认的,如何使用自己定制的首页?

  1. 在刚才的登录页配置后面指定 loginpage

    	http.formLogin().loginPage("/toLogin");
    
  2. 然后前端也需要指向我们自己定义的 login请求

    <a class="item" th:href="@{/toLogin}"> 
      <i class="address card icon"></i> 登录
    </a>
    
  3. 我们登录,需要将这些信息发送到哪里,我们也需要配置,login.html 配置提交请求及方式,方式
    必须为post:
    在 loginPage()源码中的注释上有写明:
    在这里插入图片描述

    <form th:action="@{/login}" method="post">
      <div class="field"> 
        <label>Username</label> 
        <div class="ui left icon input"> 
          <input type="text" placeholder="Username" name="username"> <!--记住这里name-->
          <i class="user icon"></i> 
        </div> 
      </div>
      <div class="field">
        <label>Password</label>
        <div class="ui left icon input">
          <input type="password" name="password"> <!--记住这里name-->
          <i class="lock icon"></i>
        </div>
      </div>
      <input type="submit" class="ui blue submit button"/> 
    </form>
    
  4. 这个请求提交上来,我们还需要验证处理,怎么做呢?我们可以查看 formLogin() 方法的源
    码!我们配置接收登录的用户名和密码的参数!

    http
      .formLogin() 
      .usernameParameter("username") //前端登录用户名 name
      .passwordParameter("password") //前端登录密码 name
      .loginPage("/toLogin")  //进入首页请求
      .loginProcessingUrl("/login"); // 首页中: 登陆表单提交请求
    
  5. 在登录页增加记住我的多选框

    <input type="checkbox" name="remember"> 记住我
    
  6. 后端处理

    //定制记住我的参数! 
    http.rememberMe().rememberMeParameter("remember");
    

附件:完整的代码

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { 
  //定制请求的授权规则 
  @Override protected void configure(HttpSecurity http) throws Exception { 
    http
      .authorizeRequests()
      .antMatchers("/")
      .permitAll() 
      .antMatchers("/level1/**").hasRole("vip1")
      .antMatchers("/level2/**").hasRole("vip2")
      .antMatchers("/level3/**").hasRole("vip3"); 
    //开启自动配置的登录功能:如果没有权限,就会跳转到登录页面! 
    // /login 请求来到登录页 
    // /login?error 重定向到这里表示登录失败 
    http.formLogin() 
      .usernameParameter("username") 
      .passwordParameter("password") 
      .loginPage("/toLogin") 
      .loginProcessingUrl("/login"); 
    // 登陆表单提交请求 
    //开启自动配置的注销的功能 
    // /logout 注销请求 
    // .logoutSuccessUrl("/"); 注销成功来到首页 
    http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交 logout请求
    http.logout().logoutSuccessUrl("/"); 
    //记住我 
    http.rememberMe().rememberMeParameter("remember"); 
  }
  //定义认证规则 
  @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
    //在内存中定义,也可以在jdbc中去拿.... 
    //Spring security 5.0中新增了多种加密方式,也改变了密码的格式。 
    //要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过 来的密码进行某种方式加密 
    //spring security 官方推荐的是使用bcrypt加密方式。 
		auth
  			.inMemoryAuthentication()
      	.passwordEncoder(new BCryptPasswordEncoder()) 
      	.withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3") 
      	.and() 
      	.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3") 
      	.and() 
      	.withUser("guest")
      	.password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2"); 
  } 
} 

番外

Spring Security 连接数据库 查询认证 关键代码

  • 配置类
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    SecurityProvider securityProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
        .authenticationProvider(securityProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/") //路径
                .permitAll() // 所有用户
                .antMatchers("/level1/**").hasRole("vip1") //vip1角色权限
                .antMatchers("/level2/**").hasRole("vip2") //vip2角色权限
                .antMatchers("/level3/**").hasRole("vip3"); //vip3角色权限

        http.rememberMe();

    }
}

  • 供应类
@Component
public class SecurityProvider implements AuthenticationProvider {

    @Autowired
    private MyUserDetailService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
        UserDetails userDetails = userDetailsService.loadUserByUsername(token.getName());  //从数据库获取用户详情
        if (userDetails == null) {
            throw new UsernameNotFoundException("找不到该用户");
        }
        if(!userDetails.getPassword().equals(token.getCredentials().toString()))
        {
            throw new BadCredentialsException("密码错误");
        }
        return new UsernamePasswordAuthenticationToken(userDetails,
                userDetails.getPassword(), //用户密码
                userDetails.getAuthorities() //用户权限
        );
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.equals(authentication);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值