安全框架 SpringSecurity 入门(超详细,IDEA2024)

目录

[零、Spring Security 简介](#零、Spring Security 简介)

一、创建一个Maven项目

1.1.添加启动器

[1.2 编写配置文件](#1.2 编写配置文件)

[1.2.1 创建配置文件](#1.2.1 创建配置文件)

[1.2.2 编写配置文件](#1.2.2 编写配置文件)

[1.3 准备控制器](#1.3 准备控制器)

[1.4 编写启动类](#1.4 编写启动类)

[1.5 准备展示页面](#1.5 准备展示页面)

[1.6 运行程序( SpringBootMain )](#1.6 运行程序( SpringBootMain ))

[二、了解 Spring Security 认证的逻辑:](#二、了解 Spring Security 认证的逻辑:)

2.1.了解页面逻辑

2.2.了解认证逻辑

[2.3.0 建表,编写实体类,数据访问层(mapper)](#2.3.0 建表,编写实体类,数据访问层(mapper))

[2.3. 了解UserDetailsService接口逻辑:](#2.3. 了解UserDetailsService接口逻辑:)

[2.3.1 创建一个业务层实现类 EmpServiceImpl,实现 UserDetailsService 接口](#2.3.1 创建一个业务层实现类 EmpServiceImpl,实现 UserDetailsService 接口)

[2.3.2 在 EmpServiceImpl 重写 loadUserByUsername 方法](#2.3.2 在 EmpServiceImpl 重写 loadUserByUsername 方法)

[2.3.2-1 分析UserDetailsService接口中方法的返回值:](#2.3.2-1 分析UserDetailsService接口中方法的返回值:)

[2.3.3 编写测试类 DemoTest](#2.3.3 编写测试类 DemoTest)

[2.3.3-1 报错:](#2.3.3-1 报错:)

[2.3.3-2 PasswordEncoder 接口](#2.3.3-2 PasswordEncoder 接口)

[2.3.3-3 了解SpringSecurity 的加密算法 ==> 密码解析器:](#2.3.3-3 了解SpringSecurity 的加密算法 ==> 密码解析器:)

[2.3.4 运行 test1](#2.3.4 运行 test1)

三、自定义登录页面

[3.1 编写登录页面( empLogin.html )](#3.1 编写登录页面( empLogin.html ))

[3.2 修改控制类( EmpController )](#3.2 修改控制类( EmpController ))

[3.3 编写自定义配置类 MyConfig](#3.3 编写自定义配置类 MyConfig)

[3.4 自定义登录成功和失败页面](#3.4 自定义登录成功和失败页面)

[3.5 编写登录成功后跳转的页面( success.html )](#3.5 编写登录成功后跳转的页面( success.html ))

[3.6 运行 SpringBootMain](#3.6 运行 SpringBootMain)

[四、记住我 功能( Remember Me )](#四、记住我 功能( Remember Me ))

[4.1. 创建 RememberMeConfig 配置类( + 建表)](#4.1. 创建 RememberMeConfig 配置类( + 建表))

[4.2 修改登录页面( empLogin.html )](#4.2 修改登录页面( empLogin.html ))

[4.3 修改配置文件( MyConfig )](#4.3 修改配置文件( MyConfig ))

[4.3.1 remember Me 功能设置](#4.3.1 remember Me 功能设置)

[4.3.2 依赖注入](#4.3.2 依赖注入)

[4.3.3 还可以设置有效时间](#4.3.3 还可以设置有效时间)

[4.3.4 运行](#4.3.4 运行)

[4.3.4-1 如果不能自动创建保存我的信息表](#4.3.4-1 如果不能自动创建保存我的信息表)

[4.4 代码没错,但就是运行不了 / 报错:](#4.4 代码没错,但就是运行不了 / 报错:)

五、授权逻辑

[5.1 新建数据库表](#5.1 新建数据库表)

[5.2 修改EmpMapper接口](#5.2 修改EmpMapper接口)

[5.3 添加 EmpMapper.xml 映射文件](#5.3 添加 EmpMapper.xml 映射文件)

[5.3.1 如果 mapper 接口上没出现小鸟图标:](#5.3.1 如果 mapper 接口上没出现小鸟图标:)

[5.4 修改 EmpServiceImpl,为用户动态添加权限](#5.4 修改 EmpServiceImpl,为用户动态添加权限)

[5.5 修改配置类:为指定路径 设置权限](#5.5 修改配置类:为指定路径 设置权限)

[5.6 运行](#5.6 运行)

[5.7 添加错误页面](#5.7 添加错误页面)

[六、 整合 Thymeleaf](#六、 整合 Thymeleaf)

[6.1 在 pom.xml 添加启动器](#6.1 在 pom.xml 添加启动器)

[6.2 修改配置类 MyConfig](#6.2 修改配置类 MyConfig)

[6.3 修改 success 页面](#6.3 修改 success 页面)

[6.4 测试结果:](#6.4 测试结果:)

[如果:ls 登录后也是四个功能:](#如果:ls 登录后也是四个功能:)

[七、退出 功能](#七、退出 功能)

[7.1 修改 success 页面:添加退出链接](#7.1 修改 success 页面:添加退出链接)

[八、 CSRF](#八、 CSRF)

[8.1 什么是 CSRF](#8.1 什么是 CSRF)

[8.2 Spring Security 中 CSRF](#8.2 Spring Security 中 CSRF)

[8.3 Spring Security 中 CSRF原理](#8.3 Spring Security 中 CSRF原理)

[8.4 实现步骤](#8.4 实现步骤)

[8.4.2 修改配置类 MyConfig](#8.4.2 修改配置类 MyConfig)

九、重新编写退出功能

[9.1 在配置类 MyConfig 添加完整的注销功能配置](#9.1 在配置类 MyConfig 添加完整的注销功能配置)

[9.2 修改成功页面( success.html )的注销功能](#9.2 修改成功页面( success.html )的注销功能)

[9.3 创建新的注销成功页面 logoutSuccess.html](#9.3 创建新的注销成功页面 logoutSuccess.html)

[9.4 运行](#9.4 运行)


零、Spring Security 简介

Spring Security 是一个高度自定义的安全框架。利用 Spring IoC/DI 和 AOP 功能,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大量重复代码的工作。

"认证(authentication)"和"授权(authorization)" (或者访问控制)是 Spring Security 重要核心功能。"认证" ,是建立一个他声明的主体的过程(一个"主体"一般是指用户,设备或一些可以在你的应用程序中执行动作的其他系统),通俗点说就是系统认为用户是否能登录。**"授权"**指确定一个主体是否允许在你的应用程序执行一个动作的过程。通俗点讲就是系统判断用户是否有权限去做某些事情。

需要有权限控制的项目都可以使用Spring Security。

一、创建一个Maven项目

1.1.添加启动器

在 pom.xml 的<project>里添加:

XML 复制代码
<!--1.继承方式添加SpringBoot启动器 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.10.RELEASE</version>
</parent>
XML 复制代码
    <!-- 2.添加Spring MVC的启动器依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.1.10.RELEASE</version>
    </dependency>

    <!-- 3.添加Mybatis 的启动器依赖-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.1</version>
    </dependency>

    <!-- 4.添加mysql数据库的驱动包依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>   <!--<version>5.1.8</version> -->
    </dependency>

    <!--5. 添加thymeleaf  的依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
        <version>2.1.10.RELEASE</version>
    </dependency>

    <!--6.添加lombok 注解依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.18</version>
        <scope>provided</scope>
    </dependency>

    <!--7.添加Spring Security启动器-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
        <version>2.1.10.RELEASE</version>
    </dependency>
XML 复制代码
<build>
    <!--添加tomcat插件 -->
    <plugins>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <port>8080</port>
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
    <!--资源拷贝的插件 -->
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.yml</include>
                <include>**/*.xml</include>
                <include>**/*.html</include> // 很重要!!
                <include>**/*.js</include> // 很重要!!
                <include>**/*.properties</include>
            </includes>
        </resource>
    </resources>
</build>

在 Maven 项目中,pom.xml文件中的标签是有特定顺序要求的,常见的标签顺序:

  1. 基础信息相关
  • modelVersion(必须,且通常为 4.0.0)
  • groupId、artifactId、version(GAV 三要素,必须)
  • packaging(可选,默认 jar)
  • name、description、url等描述信息
  1. 依赖管理相关
  • parent(父项目配置)
  • modules(聚合模块配置)
  • properties(属性定义)
  • dependencies(项目依赖)
  • dependencyManagement(依赖版本管理)
  1. 构建配置相关
  • build(构建配置,包括sourceDirectory、plugins等)
  • profiles(环境配置文件)

具体添加位置如下:

添加完刷新一下Maven(大部分不需要,我的需要这一步)

方法一:

方法二:

1.2 编写配置文件

1.2.1 创建配置文件

先在 src 的 main 包下创建 一个 resources目录

有这个 resources就直接双击,没有就自己写然后按回车,创建完长这样 ↓↓↓

如果你的 resources 长这样,没有橙色的横杠:

进入项目结构:项目--项目结构 / file(左上角的四条杠)--Project Strucrure

在 resources 下创建配置文件 application.yml (不要打错字了)

,回车

application.yml 长这样 ↓↓↓

1.2.2 编写配置文件

注意格式,注意最近,冒号后面必须有一个空格

按自己的具体情况修改端口号(8866),url中的数据库名(security),用户名,密码(root),包名(com.jr,pojo,com/jr/mapper/*.xml)等

XML 复制代码
server:
  port: 8866
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: root

mybatis:
  type-aliases-package: com.jr.pojo
  mapper-locations: classpath:com/jr/mapper/*.xml
  configuration:  ##控制台输出sql语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

1.3 准备控制器

java 复制代码
@Controller
public class EmpController {
    @RequestMapping("/show")
    public String show(){
        return "show";
    }
}

报红就点 import class

1.4 编写启动类

在 controller 包的上级包下面创建启动类 SpringBootMain并编写内容,这里的注解 @MapperScan("com.jr.mapper"),要么在这写,要么在每个mapper接口上写 @Mapper

java 复制代码
@SpringBootApplication
//@MapperScan("com.jr.mapper")
public class SpringBootMain {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootMain.class,args);
    }
}

1.5 准备展示页面

在 resources下创建 templates,在 templates 里创建show.html 页面,在body里写内容

1.6 运行程序( SpringBootMain )

在浏览器输入:127.0.0.8866/show,会自动跳到 login 页面( Spring Security 自带的 )

用户名是 user,密码是控制台里输出的 ↓↓↓ 每次都不一样

就能进到show页面了

因为密码每次都不一样,退出刷新再用这个密码就登不了了

另外,如果是直接输入 127.0.0.1:8866**/login** ,登录成功也会出现404错误

因为本身跳到 login 页面只是拦截 ,登录成功 后Spring Security默认重定向 到用户最初请求的页面 (即 /login),但由于我们并没有 实际开发**/login 这个页面** (Spring Security 自带的登录页面是框架内部的,并非我们的 show.html),所以会出现 404 错误

二、了解 Spring Security 认证的逻辑:

2.1.了解页面逻辑

在登录页右键点击查看网页源码

所以重新写登录页面时也要按这个逻辑写,表单的 method 叫 "post" ,表单的action属性指向后端处理登录请求的接口地址,用户名叫 "username",密码叫 " password" ,提交按钮的 type 要设为 "submit"

2.2.了解认证逻辑

2.3.0 建表,编写实体类,数据访问层(mapper)

emp表 Emp类

Mapper接口 :在 java.com.jr.mapper 下创建

要么每个mapper接口上都写 @Mapper注解,

要么在启动类(SpringBootMain)上面加一个注解:@MapperScan("com.jr.mapper")

java 复制代码
//@Mapper
public interface EmpMapper {
    @Select("select * from emp where username=#{username}")
    Emp selectEmpByEname(String username);
}

2.3. 了解UserDetailsService接口逻辑:

2.3.1 创建一个业务层实现类 EmpServiceImpl,实现 UserDetailsService 接口

当什么也没有配置 的时候,账号和密码是由 Spring Security 定义生成的。而在实际项目 中账号和密码都是从数据库 中查询出来的。 所以我们要通过自定义逻辑 控制认证逻辑。如果需要自定义逻辑时,只需要实现 UserDetailsService 接口即可。

<-- 创建,↓实现接口(+ @Service 注解)

按住 Ctrl点击 UserDetailsService 进入 UserDetailsService 接口查看 ↓↓↓

因为数据库中存入的密码是加密的,用户输入内容在数据库中是查询不到的。

2.3.2 在 EmpServiceImpl 重写 loadUserByUsername 方法

右键点击Generate / alt+insert --> Override Methods --> 重写 loadUserByUsername 方法​​​​​ 一整个EmpServiceImpl的代码 ↓↓↓

java 复制代码
@Service
public class EmpServiceImpl implements UserDetailsService {

    @Autowired
    private EmpMapper empMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        Emp emp = empMapper.selectEmpByEname(s);
        if (emp == null) {
            throw new UsernameNotFoundException("用户名不存在,登录失败!");
        }

        // AuthorityUtils.NO_AUTHORITIES:默认
        UserDetails user = new User(emp.getUsername(), emp.getPassword(), AuthorityUtils.NO_AUTHORITIES);
        return user;
    }
}
2.3.2-1 分析UserDetailsService接口中方法的返回值:

loadUserByUsername 方法的返回值 UserDetails 是一个接口 ↓↓↓

UserDetails 对象是SpringSecurity封装的,包含了 username 查到的对象的所有信息。

要想返回 UserDetails 的实例就只能返回接口的实现类。Spring Security 中提供了如下的实例。对于我们只需要使用里面的User 类即可。注意 User 的全限定路径是:

org.springframework.security.core.userdetails.User 此处经常和系统中自己开发的 User 类弄混。 不要导错包了!!

其中构造方法有两个,调用其中任何一个都可以实例化UserDetails实现类User类的实例。而三个参数的构造方法实际上也是调用7个参数的构造方法。

username:用户名, password:密码, authorities:用户具有的权限。此处不允许为null

此处的用户名 应该是客户端传递 过来的用户名。而密码 应该是从数据库中查询出来的密码。Spring Security 会根据 User 中的 password 和客户端传递过来的 password 进行比较。如果相同则表示认证通过,如果不相同表示认证失败。

2.3.3 编写测试类 DemoTest

在 test.java.com.jr 下创建测试类 DemoTest

java 复制代码
public class DemoTest {
    @Test
    public void test1(){
        // 创建解析器
        PasswordEncoder encoder = new BCryptPasswordEncoder();

        // 对密码进行加密
        String password = encoder.encode("123");
        System.out.println("------------"+password);


        // 判断原字符加密后和内容是否匹配
        boolean result = encoder.matches("123",password);
        System.out.println("============="+result);
    }
}
2.3.3-1 报错:

@Test 报红:导包

test1 方法前面没有运行按钮(绿色的三角)就到 pom.xml 里添加依赖

XML 复制代码
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope> <!-- 仅在测试环境生效,不打包到生产 -->
    </dependency>

install 后控制台报错:程序包 org.junit不存在

修改 pom.xml 中 junit 依赖的版本(可能是版本低了)修改后刷新 Maven,重新clean,install

-->

2.3.3-2 PasswordEncoder 接口

encode():把参数按照特定的解析规则进行解析。

matches():验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回 true;如果不匹配,则返回 false。第一个参数表示需要被解析的密码。第二个参数表示存储的密码。

upgradeEncoding():如果解析的密码能够再次进行解析且达到更安全的结果则返回 true,否则返回 false。默认返回 false。

2.3.3-3 了解SpringSecurity 的加密算法 ==> 密码解析器:

Spring security 中有多种密码加密方式,MD5 算法的 Md5PasswordEncoder、SHA 算法的 ShaPasswordEncoder,但由于是弱加密算法都被弃用了。推荐使用的是 BCrypt 算法的 BCryptPasswordEncoder

​ BCryptPasswordEncoder是Spring Security官方推荐的密码解析器,平时多使用这个解析器。BCryptPasswordEncoder是对bcrypt强散列方法的具体实现。是基于Hash算法实现的单向加密。可以通过strength控制加密强度,默认10。

2.3.4 运行 test1

右下角的弹窗都点这个 Enable annotation processing 就好了

输出的是 123 加密后的字符串,把这个写到 Emp 表的 password 字段(每次运行出来的都不一样,存哪个都行)

为什么加密同一个内容 ,多次执行后,结果不一样 ?因为概率问题,假如两个人的密码一样,加密后也一样。一旦知道其中一个人的密码,那么就会知道其他人的,不安全。

三、自定义登录页面

3.1 编写登录页面( empLogin.html )

在 templates下创建 empLogin.html

在 body 里写:

html 复制代码
<b>这是一个(相对)漂亮的登录页面!</b>
<hr/>
<form action="/elogin" method="post">
    员工姓名:<input type="text" name="username"/><br/>
    员工姓名:<input type="text" name="password"/><br/>
    <input type="submit" value="登录"/>
</form>

表单提交地址不指定默认是login;此时访问当前登录页面,需要地址栏输入具体页面名称和地址访问。

3.2 修改控制类( EmpController )

java 复制代码
@Controller
public class EmpController {
    @RequestMapping("/{url}")
    public String show(@PathVariable String url){
        return url;
    }
}

有了这个控制器类之后,地址里输入页面名称就可以,这样 ↓↓↓( 这是 MyConfig )

3.3 编写自定义配置类 MyConfig

在 src.main.java.com.jr 下创建 config 包,在里面创建 MyConfig

继承 WebSecurityConfigurerAdapter 类,重写 configure 方法(参数类型是 HttpSecurity,不要选错了),所有需要自定义 的地方都在这方法里配置

一整个 MyConfig 的代码 ↓↓↓

java 复制代码
@Configuration
public class MyConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //1.配置表单认证:
        http.formLogin()
                .loginProcessingUrl("/elogin") //配置自定义登录页面中 表单的action提交地址。使用默认/login,可以不进行配哦之
                .loginPage("/empLogin")    //登录页面对应地址
                .successForwardUrl("/success") //设置登录成功后的跳转页面
                .failureForwardUrl("/empLogin");//设置登录失败后的跳转页面,也可以再整一个 failure.html 页面

        //2.配置权限:
        http.authorizeRequests()
                .antMatchers("/empLogin").permitAll() //设置路径放行
                .anyRequest().authenticated();  //设置路径拦截

        //3.关闭csrf防护
        http.csrf().disable();
    }

    /*指定SpringSecurity认证的加密算法*/
    @Bean
    public BCryptPasswordEncoder getBCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

3.4 自定义登录成功和失败页面

3.5 编写登录成功后跳转的页面( success.html )

位置: 内容:

3.6 运行 SpringBootMain

在网址栏输入 127.0.0.1:8866/show ,会自动跳转到 127.0.0.1:8866/empLogin 页面

按 emp 表里的,输入 zs 和 123 再输入 127.0.0.1:8866/show 就可以跳转了

四、记住我 功能( Remember Me )

4.1. 创建 RememberMeConfig 配置类( + 建表)

在 config 包下创建 RememberMeConfig

java 复制代码
@Configuration
public class RememberMeConfig {
    @Autowired
    private DataSource dataSource;
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl();
        jdbcTokenRepositoryImpl.setDataSource(dataSource);
        // 自动 建表 ,第一次启动时需要,第二次启动时注释掉 ↓
        jdbcTokenRepositoryImpl.setCreateTableOnStartup(true);
        return jdbcTokenRepositoryImpl;
    }
}

其中,倒数第二行的 jdbcTokenRepositoryImpl.setCreateTableOnStartup(true); 这段是用来建表 的,第一次运行时会建表,运行完要注释掉,不然下次运行会报错。

↓↓↓ 报的错有点多,主要的是最后一张里标出来的那个 Table 'persistent_logins' already exists

4.2 修改登录页面( empLogin.html )

添加代码:记住我:<input type="checkbox" name="remember-me" value="true"/> <br/>

4.3 修改配置文件( MyConfig )

4.3.1 remember Me 功能设置

java 复制代码
        //3.remember Me功能设置:
        http.rememberMe()
                .userDetailsService(empService) // 登录逻辑交给哪个对象
                .tokenRepository(repository)   // 持久层对象
                .tokenValiditySeconds(60*30);   //设置 remember Me 的 token 值有效时间为30分钟

位置:

报红:在 MyConfig 类里加下面这个,进行自动配置↓↓↓

4.3.2 依赖注入

java 复制代码
    @Autowired
    private PersistentTokenRepository repository;
    @Autowired
    private EmpServiceImpl empService;
    //EmpServiceImpl实现了SpringSecurity的认证接口的实现类

位置:

4.3.3 还可以设置有效时间

默认的有效时间是 14 天(1209600 秒)

上面( 4.3.1 )的 .tokenValiditySeconds(60*30) 就是,单位是秒

【说明】:重启服务器之后,仍然可以记住你!访问 /show,可以直接跳转到 show 页面,不会再要求登录了。

4.3.4 运行

先运行一遍 -- 建表)

到 RemenbermeConfig 注释掉建表的代码

在网址栏里输入:127.0.0.1:8866/show ,拦截到登录页面,输入信息,勾选"记住我"选项,登录

运行完去数据库找 persistent_logins 表:↓ 保存了这次登录的数据

重启项目(确定把建表的代码给注掉了),直接访问 127.0.0.1:8866/show 不会被拦截

4.3.4-1 如果不能自动创建保存我的信息表

可以使用建表语句:

sql 复制代码
-- 创建数据库 security ,创建数据库表persistent_logins语句:

create table persistent_logins (
username varchar(64) not null, 
series varchar(64) primary key, 
token varchar(64) not null, 
last_used timestamp not null)

4.4 代码没错,但就是运行不了 / 报错:

在生命周期里 cleaninstall:↓ 在项目右侧找到 "m",单击 1,2~5 双击

又或者试试刷新一下 maven 项目(然后再 clean,install ):

方法一

方法二:在项目右击,滑到底部找到 Maven,点击 Sync Project

五、授权逻辑

5.1 新建数据库表

5.2 修改EmpMapper接口

java 复制代码
//@Mapper
public interface EmpMapper {
    @Select("select * from emp where username=#{username}")
    Emp selectEmpByEname(String username);
    /**
     * 根据用户名查询用户权限
     * @param username
     * @return
     */
    List<String> selectPnameByUsername(String username);
}

5.3 添加 EmpMapper.xml 映射文件

resourcescom\jr\mapper 下创建(这里必须用斜杠)

xml 映射文件的路径要跟 Mapper 接口的一样,名字、方法名、参数类型、返回值类型都要一样

-->

XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jr.mapper.EmpMapper">
    <select id="selectPnameByUsername" resultType="string" parameterType="string">
        select p.pname
        FROM emp e
                 inner join role_emp re on e.eid=re.eid
                 inner join role r on re.rid=r.rid
                 inner join role_power rp on r.rid=rp.rid
                 inner join power p on rp.pid=p.pid
        where e.username=#{username}
    </select>
</mapper>

5.3.1 如果 mapper 接口上没出现小鸟图标:

刷新 Maven ( 本篇的 4.4 )

5.4 修改 EmpServiceImpl,为用户动态添加权限

java 复制代码
@Service
public class EmpServiceImpl implements UserDetailsService {

    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private Emp emp;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        Emp emp = empMapper.selectEmpByEname(s);
        if (emp == null) {
            throw new UsernameNotFoundException("用户名不存在,登录失败!");
        }

// ********这下面是添加,修改的********

        // 查询用户对应的权限
        List<String> listPermission = empMapper.selectPnameByUsername(s);
        List<SimpleGrantedAuthority> listAuthority = new ArrayList<SimpleGrantedAuthority>();
        for(String permisssion : listPermission){
            listAuthority.add(new SimpleGrantedAuthority(permisssion));
        }
        return new org.springframework.security.core.userdetails.User(emp.getUsername(),emp.getPassword(),listAuthority);

        // AuthorityUtils.NO_AUTHORITIES:默认
        /*UserDetails user = new User(emp.getUsername(), emp.getPassword(), AuthorityUtils.NO_AUTHORITIES);
        return user;*/
    }
}

5.5 修改配置类:为指定路径 设置权限

修改 MyConfig类的 "2.配置权限" 部分:( 就是添加 "/show" 那一行 )

意思是只有 拥有 emp:remove 权限的用户才能登录 show 页面

java 复制代码
//2.配置权限:
http.authorizeRequests()
        .antMatchers("/empLogin").permitAll() //设置路径放行
        .antMatchers("/show").hasAuthority("emp:remove") //为指定路径,设置权限
        .anyRequest().authenticated();  //设置路径拦截

5.6 运行

在浏览器中输入http://localhost:8866/show 会要求进行登录认证,认证后如果用户具有emp:remove 权限会正常访问控制器,如果没有权限会报 403

· 用户 zs 登录,zs 是 rid 为 1 的管理员 ,有 pid 为 4 的 emp:remove 权限,登录成功后访问 show 页面会成功访问

-->

· 用户 ls 登录,ls 是 rid 为 2 的普通用户 ,没有 pid 为 4 的 emp:remove 权限,登录成功后访问 show 页面会报403 错误(权限不匹配)

-->

5.7 添加错误页面

方式一:设置具体的状态码页面

在 templates下,创建 error 文件夹,创建 错误状态码 .html 页面。 500----》500.html 404----》404.html

方式二:使用X进行模糊匹配

在 templates下,创建 error 文件夹,创建 数字+通配符 .html 页面。 535------5XX.html 404------4XX.htm

注意:40X 或者50X的格式是错误的!

方式三:统一错误显示页面

在 templates下,创建 error.html 页面。页面显示优先级:方式一(具体状态码页面) > 方式二(X模糊匹配) > 方式三(统一的错误显示页面)

在 pom.xml 文件的最底下的 <includes> 里配置资源文件的包含规则,使它可以识别后缀为 .png 和 .jpg 的文件:(添加后刷新maven)

XML 复制代码
          <include>**/*.png</include>  
          <include>**/*.jpg</include> 

添加位置:

运行:登录 ls ,访问 show 页面

六、 整合 Thymeleaf

Spring Security 可以在一些视图技术中进行控制显示效果。例如:JSP 或 Thymeleaf。在非前后端分离且使用 Spring Boot 的项目中多使用 Thymeleaf 作为视图展示技术。

Thymeleaf 对 Spring Security 的支持都放在 thymeleaf-extras-springsecurityX 中,目前最新版本为 5。所以需要在项目中添加此 jar 包的依赖和 thymeleaf 的依赖。

6.1 在 pom.xml 添加启动器

然后刷新Maven

XML 复制代码
    <dependency>
      <groupId>org.thymeleaf.extras</groupId>
      <artifactId>thymeleaf-extras-springsecurity5</artifactId>
      <version>3.0.4.RELEASE</version>
    </dependency>

6.2 修改配置类 MyConfig

java 复制代码
        //2.配置权限:
        http.authorizeRequests()
                .antMatchers("/empLogin").permitAll() //设置路径放行
                .antMatchers("/show").hasAuthority("emp:findAll") //认证后,为指定路径,设置权限
                .antMatchers("/save").hasAuthority("emp:save")
                .antMatchers("/remove").hasAuthority("emp:remove")
                .antMatchers("/edit").hasAuthority("emp:edit")
                .anyRequest().authenticated(); //设置路径拦截,认证后才可以访问!

添加对应的页面:save.html,remove.html,edit.html

6.3 修改 success 页面

最顶上的 xmlns:th 和 xmlns:sec :在html页面中引入 thymeleaf 命名空间和 security 命名空间

html 复制代码
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<a href="/save" sec:authorize="hasAuthority('emp:save')">添加</a>
<a href="/edit" sec:authorize="hasAuthority('emp:edit')">修改</a>
<a href="/show" sec:authorize="hasAuthority('emp:findAll')">查询</a>
<a href="/remove" sec:authorize="hasAuthority('emp:remove')">删除</a>

</body>
</html>

6.4 测试结果:

不用权限的用户,登录成功后,看到的success页面的链接个数,是不一样的

如果:ls 登录后也是四个功能:

1、确认数据库中的 ls 对应的rid(1是管理员,2是普通用户)

2、在 pom.xml 中确认thymeleaf 依赖的版本:是 <version>2.1.10.RELEASE</version>

七、退出 功能

用户只需要向 Spring Security 项目中发送 /logout 退出请求即可。

7.1 修改 success 页面:添加退出链接

html 复制代码
<br/>
<a href="/logout">退出登录</a>

【说明】:点击 /logout 后,Spring Security 默认自动跳转到 login 页面!当然也可以设置自定义跳转策略,设置自定义退出登录页面 ; 同时还会删除掉数据库中,Remember Me 功能的数据!

登录后,退出前的 persistent_logins 表:刷新 --> 多一条数据(过期的不会自动删除,会留着

点击退出登录回到登录页面 ↓

刷新表:zs 的数据都删没了

八、 CSRF

8.1 什么是 CSRF

CSRF(Cross-site request forgery)跨站请求伪造,是通过伪造用户请求访问受信任站点的非法请求访问。

跨域:只要网络协议,ip地址,端口中任何一个不相同就是跨域请求。

客户端与服务进行交互时,由于 http 协议本身是无状态协议,所以引入了cookie 进行记录客户端身份。在 cookie 中会存放 session id 用来识别客户端身份的。在跨域的情况下,session id 可能被第三方恶意劫持,通过这个 session id 向服务端发起请求时,服务端会认为这个请求是合法的,可能发生很多意想不到的事情。

8.2 Spring Security 中 CSRF

从Spring Security4 开始 CSRF 防护默认开启。默认会拦截请求。进行 CSRF 处理。CSRF 为了保证不是其他第三方网站访问,要求访问时携带参数名为 _csrf 值为 token(token 在服务端产生)的内容,如果 token 和服务端的 token 匹配成功,则正常访问。

8.3 Spring Security 中 CSRF原理

  1. 当服务器加载登录页面。(loginPage中的值,默认/login),先生成 csrf 对象,并放入作用域中,key 为 _csrf。之后会对 ${_csrf.token} 进行替换,替换成服务器生成的 token 字符串。

  2. 用户在提交登录表单时,会携带 csrf 的 token。如果客户端的 token 和服务器的 token 匹配说明是自己的客户端,否则无法继续执行。

  3. 用户退出的时候,必须发起 POST 请求,且和登录时一样,携带 csrf 的令牌。

8.4 实现步骤

8.4.1 所有页面都要添加 csrf 防护:

html 复制代码
<html lang="en" xmlns:th="http://www.thymeleaf.org">
html 复制代码
    <input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}"/>

8.4.2 修改配置类 MyConfig

取消:关闭 csrf 防护

九、重新编写退出功能

额。。退出功能出错了,重新写了一个

9.1 在配置类 MyConfig 添加完整的注销功能配置

java 复制代码
        //4.配置注销功能
        http.logout()
            .logoutUrl("/logout")  // 设置注销的URL
            .logoutSuccessUrl("/logoutSuccess")  // 注销成功后跳转到注销成功页面
            .invalidateHttpSession(true);  // 使会话失效

这样 ↓ 更新了权限配置,允许匿名访问 /logoutSuccess 路径

9.2 修改成功页面( success.html )的注销功能

java 复制代码
<form th:action="@{/logout}" method="post">
    <input type="submit" value="退出登录"/>
    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
</form>

这样 ↓

9.3 创建新的注销成功页面 logoutSuccess.html

html 复制代码
    <h1>注销成功!</h1>
    <p>您已成功退出系统。</p>
    <a href="/empLogin">返回登录页面</a>

9.4 运行

ok呀差不多了,要是还有什么错误可以评论或者私信我

之后要是发现什么问题了也会进行修改更新的

相关推荐
孤独斗士7 小时前
解决Intellij IDEA控制台,logger.info(),system.out.println()等中文乱码问题
java·ide·intellij-idea
SimonKing7 小时前
SpringBoot集成:5分钟实现HTML转PDF功能
java·后端·程序员
wuxuanok7 小时前
苍穹外卖 —— 公共字段填充
java·开发语言·spring boot·spring·mybatis
串串店藕片打孔员8 小时前
把List<T>构建一颗树封装工具类
java
用户0332126663678 小时前
自动创建 Word 文档——Java 实现
java
Cikiss8 小时前
图解 bulkProcessor(调度器 + bulkAsync() + Semaphore)
java·分布式·后端·elasticsearch·搜索引擎
LL_break8 小时前
线程1——javaEE 附面题
java·开发语言·面试·java-ee
王中阳Go8 小时前
面试官:“聊聊最复杂的项目?”90%的人开口就凉!我面过最牛的回答,就三句话
java·后端·面试
玉衡子8 小时前
一、Java类加载机制
java