第20章 Springboot整合Spring Security实现安全控制和权限方案

1,394 阅读6分钟

任务描述

任务要求

使用IDEA开发工具构建一个项目多模块工程。study-springboot-chapter14学习关于Springboot集成Security如何实现安全认证和授权知识点

  1. 基于study-springboot工程,复制study-springboot-chapter00标准项目,坐标groupId(com.cbitedu)、artifactId(study-springboot-chapter14),其他默认
  2. 继承study-springboot工程依赖
  3. 引入Spring security安全框架,实现系统认证和授权

任务收获

  1. Spring Boot中整合Spring security

  2. 学会使用JUnit完成单元测试

  3. Spring security四种授权模式:

    1. 内存用户存储
    2. 数据库用户存储
    3. LDAP用户存储
    4. 自定义用户存储
  4. 掌握web开发原理

  5. Spring Security+Thymeleaf标签相结合实现权限

  6. Spring Security 的注解,实现权限控制

  7. 熟悉SpringMVC的基础配置

任务准备

环境要求

  1. JDK1.8+
  2. MySQL8.0.27+
  3. Maven 3.6.1+
  4. IDEA/VSCode

工程目录要求

study-springboot-chapter14

任务实施

在所有的开发的系统中,都必须做认证(authentication)和授权(authorization),以保证系统的安全性。

  1. authentication: 【认证】你要登录论坛,输入用户名张三,密码 1234,密码正确,证明你张三确实是张三,这就是 authentication。
  2. authorization:【授权】再一 check 用户张三是个版主,所以有权限加精删别人帖,这就是 authorization 。

所以简单来说:认证解决“你是谁”的问题,授权解决“你能做什么”的问题。

方法一:thymeleaf标签+Spring Security实现

第一步:基于标准Springboot工程模板study-springboot-chapter00继承父工程,在pom.xml中加入所需的依赖:

  <!-- 实现对 Spring Security 的自动化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>

第二步:application.yml文件中注入Thymeleaf相关配置,默认只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染。

#服务配置
server:
  port: 81
#   #设置日志相关打印sql 语句
logging:
  level:
    com.cbitedu: debug
    org.springframework.web: debug
#关闭运行日志图标(banner)
spring:
  datasource:
    url:  jdbc:mysql://localhost:3306/platform?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username:  root
    password:  root
    driver-class-name:  com.mysql.cj.jdbc.Driver
    #Thymeleaf模板引擎相关配置
    thymeleaf:
      #prefix:指定模板所在的目录
      prefix: classpath:/templates/
      #check-tempate-location: 检查模板路径是否存在
      check-template-location: true
      #cache: 是否缓存,开发模式下设置为false,避免改了模板还要重启服务器,线上设置为true,可以提高性能。
      cache: false
      #suffix 配置模板后缀名
      suffix: .html
      encoding: UTF-8
      mode: HTML5
    devtools:
      restart:
        enabled: true  #设置开启热部署
        additional-paths: src/main/java #重启目录
        # exclude: static/**,public/**,db/**,i18n/**,templates/**  #排除文件(不重启项目)

第三步:启用Spring Security配置类WebSecurityConfig

package com.cbitedu.springboot.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
//启用安全认证
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 认证请求规则
     *#(String... antPatterns) 方法,配置匹配的 URL 地址,基于 Ant 风格路径表达式 ,可传入多个。
     * 【常用】#permitAll() 方法,所有用户可访问。
     * 【常用】#denyAll() 方法,所有用户不可访问。
     * 【常用】#authenticated() 方法,登录用户可访问。
     * #anonymous() 方法,无需登录,即匿名用户可访问。
     * #rememberMe() 方法,通过 remember me 登录的用户可访问。
     * #fullyAuthenticated() 方法,非 remember me 登录的用户可访问。
     * #hasIpAddress(String ipaddressExpression) 方法,来自指定 IP 表达式的用户可访问。
     * 【常用】#hasRole(String role) 方法, 拥有指定角色的用户可访问。
     * 【常用】#hasAnyRole(String... roles) 方法,拥有指定任一角色的用户可访问。
     * 【常用】#hasAuthority(String authority) 方法,拥有指定权限(authority)的用户可访问。
     * 【常用】#hasAuthority(String... authorities) 方法,拥有指定任一权限(authority)的用户可访问。
     * 【最牛】#access(String attribute) 方法,当 Spring EL 表达式的执行结果为 true 时,可以访问。
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("VIP1")
                .antMatchers("/level2/**").hasRole("VIP2")
                .antMatchers("/level3/**").hasRole("VIP3");
        // 注销账号
        http.logout().logoutSuccessUrl("/");
        /****************** 默认的 ****************/
        // 默认登录表单
         http.formLogin();
        // 记住我
         http.rememberMe();

        /****************** 定制的 ****************/
        // 定制页面和参数,默认名称:username,password
        // http.formLogin().loginPage("/login").usernameParameter("userName").passwordParameter("userPwd");
        // 定制记住我
        // http.rememberMe().rememberMeParameter("remember");
    }

    /**
     * 授权
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //内存用户存储
        auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
                .withUser("devadmin").password("123456").roles("VIP1", "VIP2")
                .and()
                .withUser("normal").password("123456").roles("VIP2", "VIP3")
                .and()
                .withUser("test").password("123456").roles("VIP1", "VIP3");
    }
}
第四步,页面控制类 PageController
package com.cbitedu.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class PageController {

    @GetMapping({"/", "", "/index"})
    public String index() {
        return "index";
    }

    // 定制的登录表单
    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @GetMapping("level1")
    @ResponseBody
    public String level1() {
        return "level1 拥有角色VIP1";
    }

    @GetMapping("level2")
    @ResponseBody
    public String level2() {
        return "level2 拥有角色VIP2";
    }

    @GetMapping("level3")
    @ResponseBody
    public String level3() {
        return "level3 拥有角色VIP3";
    }
}

 

第五步:创建登录表单

我们在 resource/templates 目录下新建书籍列表页面 login.html,index.html代码如下:

login.html代码如下:

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>定制的登录表单</title>
</head>
<body>
<h2 align="center">定制登录页面</h2>
<hr>
<form th:action="@{/login}" method="post" style="margin: auto; width: 200px;">
    用户名:<input type="text" name="userName"><br>
    密码:<input type="password" name="userPwd"><br>
    <input type="checkbox" name="remember"> 记住我<br>
    <input type="submit" value="登录">
</form>
</body>
</html>

index.html源代码如下:

<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>SpringBoot 整合 SpringSecurity</title>
</head>
<body>
<h2 align="center">SpringBoot 整合 SpringSecurity 实现登录、授权案例</h2>
<div sec:authorize="!isAuthenticated()">
    <h4 align="center">游客您好,<a th:href="@{/login}">请登录</a></h4>
</div>
<div sec:authorize="isAuthenticated()">
    <h4><span sec:authentication="name"></span>,您拥有的角色:<span sec:authentication="principal.authorities"></span></h4>
    <form th:action="@{/logout}" method="post">
        <input type="submit" value="注销"/>
    </form>
</div>
<hr>
<ul>
    <div sec:authorize="hasRole('VIP1')">
        <li><a th:href="@{/level1}">VIP1,可以访问</a></li>
    </div>
    <div sec:authorize="hasRole('VIP2')">
        <li><a th:href="@{/level2}">VIP2,可以访问</a></li>
    </div>
    <div sec:authorize="hasRole('VIP3')">
        <li><a th:href="@{/level3}">VIP3,可以访问</a></li>
    </div>
</ul>
</body>
</html>

此时我们启动项目,然后访问 http://localhost:81/ ,账号:admin/123456

切换不同用户验证结果。

小结

  • Spring security认证请求规则
    *#(String... antPatterns) 方法,配置匹配的 URL 地址,基于 Ant 风格路径表达式 ,可传入多个。
  • 【常用】#permitAll() 方法,所有用户可访问。
  • 【常用】#denyAll() 方法,所有用户不可访问。
  • 【常用】#authenticated() 方法,登录用户可访问。
  • #anonymous() 方法,无需登录,即匿名用户可访问。
  • #rememberMe() 方法,通过 remember me 登录的用户可访问。
  • #fullyAuthenticated() 方法,非 remember me 登录的用户可访问。
  • #hasIpAddress(String ipaddressExpression) 方法,来自指定 IP 表达式的用户可访问。
  • 【常用】#hasRole(String role) 方法, 拥有指定角色的用户可访问。
  • 【常用】#hasAnyRole(String... roles) 方法,拥有指定任一角色的用户可访问。
  • 【常用】#hasAuthority(String authority) 方法,拥有指定权限(authority)的用户可访问。
  • 【常用】#hasAuthority(String... authorities) 方法,拥有指定任一权限(authority)的用户可访问。
  • 【最牛】#access(String attribute) 方法,当 Spring EL 表达式的执行结果为 true 时,可以访问。

实验实训

1、重点学习Spring Security使用

2、利用Spring Security注解EnableGlobalMethodSecurity实现权限控制:

  • 安全配置类:SecurityConfig
  • 控制类:NoSecurityController、SecurityController

通过上述步骤,我们已经成功地在Spring Boot应用中集成了Spring Security,并实现了基本的认证与授权功能,同时也掌握了如何使用Thymeleaf与Spring Security结合来控制页面的访问权限。接下来,我们将探索另一种安全解决方案——集成Shiro,它将为我们提供另一套强大的安全防护机制,让我们在不同的场景下能够更加灵活地选择合适的安全框架来保护应用。在转向Shiro之前,让我们先回顾一下所学的Spring Security要点,并准备好迎接新的挑战。