Springboot集成SpringSecurity入门

SpringSecurity概述

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

需要牢记的几个类:

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

Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)

“授权” (Authorization)

授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。

这个概念是通用的,而不是只在Spring Security 中存在。

“认证”(Authentication)

身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。

身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。

框架核心组件

核心组件

  • SecurityContextHolder: 提供对SecurityContext的访问
  • SecurityContext: 持有Authentication对象和其他可能需要的信息
  • AuthenticationManager: 其中可以包含多个AuthenticationProvider
  • ProviderManager: 对象为AuthenticationManager接口的实现类
  • AuthenticationProvider: 主要用来进行认证操作的类 调用其中的authenticate()方法去进行认证操作
  • Authentication: Spring Security方式的认证主体
  • GrantedAuthority: 对认证主题的应用层面的授权,含当前用户的权限信息,通常使用角色表示
  • UserDetails:构建Authentication对象必须的信息,可以自定义,可能需要访问DB得到
  • UserDetailsService:通过username构建UserDetails对象,通过loadUserByUsername根据userName获取UserDetail对象 (可以在这里基于自身业务进行自定义的实现 如通过数据库,xml,缓存获取等)
    img

过滤器总结

  • WebAsyncManagerIntegrationFilter
  • SecurityContextPersistenceFilter
  • HeaderWriterFilter
  • CorsFilter
  • LogoutFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • AnonymousAuthenticationFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • UsernamePasswordAuthenticationFilter
  • BasicAuthenticationFilter

[外链图片转存中…(img-Y2qV9S3F-1592456960227)]

SpringSecurity测试环境部署

导入依赖

导入 thymeleaf依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

在application.properties 中关闭 thymeleaf 缓存

spring.thymeleaf.cache=false

创建静态页面

创建过个静态页面供SpringSecurity控制访问

分别在template目录下创建文件夹 views,在views中创建三个 level01、level02、level03包,然后分别在每个包下创建三个静态页面1.html、2.html、3.html

然后在每个静态页面填写不同的内容加以区分

1.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>level01-1.html</h1>
    <hr>
    <a th:href="@{/index}">返回主页</a>
</body>
</html>

创建 index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>hello SpringSecurity</h1>
    <hr>
    <div style="text-align: center; margin-top: 100px">
        <div>
            <p>
                <a th:href="@{/level01/1}">level 01-1</a>
                <a th:href="@{/level01/2}">level 01-2</a>
                <a th:href="@{/level01/3}">level 01-3</a>
            </p>
        </div>
        <div>
            <p>
                <a th:href="@{/level02/1}">level 02-1</a>
                <a th:href="@{/level02/2}">level 02-2</a>
                <a th:href="@{/level02/3}">level 02-3</a>
            </p>
        </div>
        <div>
            <p>
                <a th:href="@{/level03/1}">level 03-1</a>
                <a th:href="@{/level03/2}">level 03-2</a>
                <a th:href="@{/level03/3}">level 03-3</a>
            </p>
        </div>
    </div>
</body>
</html>

创建控制器

创建Controller 实现对各个页面的跳转操作

package com.jason.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @authot jason_yan
 * @date 2020/6/18-9:08
 */
@Controller
public class IndexController {

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

    @RequestMapping("/level01/{id}")
    public String level1(@PathVariable("id") int id){
        return "views/level01/"+id;
    }

    @RequestMapping("/level02/{id}")
    public String level2(@PathVariable("id") int id){
        return "views/level02/"+id;
    }

    @RequestMapping("/level03/{id}")
    public String level3(@PathVariable("id") int id){
        return "views/level03/"+id;
    }
}

Springboot整合SpringSecurity

导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

此时启动服务器时,SpringSecurity 会拦截所有的请求,用户必须登录SpringSecurity 的 默认登录页面,才可以访问其他资源

账号:user

密码:控制台自动生成

创建SpringSecurity配置类

SecurityConfig配置类继承WebSecurityConfigurerAdapter

重写两个方法:

configure(HttpSecurity http):进行用户授权(验证某个用户是否有权限执行某个操作)

configure(AuthenticationManagerBuilder auth):进行用户认证(检测登录用户是否合法)

在配置类上添加 @EnableWebSecurity的作用:

​ 1: 加载WebSecurityConfiguration配置类, 配置安全认证策略。

​ 2: 加载AuthenticationConfiguration, 配置了认证信息。

package com.jason.controller.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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @authot jason_yan
 * @date 2020/6/18-9:54
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 设置请求资源的权限(授权)
        http
                .authorizeRequests() // 对请求进行授权
                .antMatchers("/").permitAll() // 设置首页任何人都可访问
                .antMatchers("/level01/**").hasRole("doctor") // 指定要设置权限信息的地址和授权角色
                .antMatchers("/level02/**").hasRole("master") // 两个*代表目录的深度(任意深度)
                .antMatchers("/level03/**").hasRole("teacher")
                .and()
                .formLogin(); // 设置未授权的请求跳转 到 登录表单页面

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        // 模拟假数据
        auth.inMemoryAuthentication()
                .withUser("zs").password("111").roles("doctor")
                .and()
                .withUser("ls").password("222").roles("master")
                .and()
                .withUser("ww").password("333").roles("teacher");
    }
}

测试以上配置:

1、当请求首页上的链接时,会跳转到SpringSecurity 默认登录页面,因为我们没有相应的权限访问该资源————.formLogin() 实现本次跳转

2、可以使用 zs、ls、ww等用户进行登录

3、登录成功后无法访问相对应的角色资源:报错 java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”

解决上述问题:在登录过程中,SpringSecurity处理的密码不能是明文密码,所以我们的密码需要 在认证方法中 进行对密码进行加密

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    // 模拟假数据
    auth.inMemoryAuthentication()
        .passwordEncoder(bCryptPasswordEncoder) // 设置加密方式
        .withUser("zs").password(bCryptPasswordEncoder.encode("111")).roles("doctor")
        .and()
        .withUser("ls").password(bCryptPasswordEncoder.encode("222")).roles("master")
        .and()
        .withUser("ww").password(bCryptPasswordEncoder.encode("333")).roles("teacher");
}

以上即可实现相应的用户 访问相应的资源

设置用户访问权限不足的跳转页面

在SecurityConfig配置类的configure(HttpSecurity http)方法中

添加 当用户权限不足时 的访问路径

@Override
protected void configure(HttpSecurity http) throws Exception {

    // 设置请求资源的权限(授权)
    http
        .authorizeRequests() // 对请求进行授权
        ...
        .formLogin(); // 设置未授权请求跳转的登录表单页面

    http.exceptionHandling().accessDeniedPage("/no/auth"); // 用有访问权限的角色被访问拒绝后跳转的路径

}

控制器 实现跳转

@RequestMapping("/no/auth")
public String noAuth(){
    return "views/no-auth";
}

在views目录下创建 跳转的页面 no-auth.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>您的权限不足以访问此资源</h1>
    <hr>
    <a th:href="@{/index}">返回主页</a>
</body>
</html>

注销功能

在 SecurityConfig 配置类的 configure(HttpSecurity http) 方法中

开启注销功能

@Override
protected void configure(HttpSecurity http) throws Exception {

    ...
    
    http.logout()		//开启用户退出登录的功能
        .logoutSuccessUrl("/");	//设置成功退出登录后前往首页
    
    // 下方无代码

}

在index.html页面中添加 退出登录的请求链接地址

<body>
    <h1>hello SpringSecurity</h1>
    <hr>
    <p  style="text-align: right; margin-right: 20px">
        <a th:href="@{/logout}">退出登录</a>
    </p>
    
    <div style="text-align: center; margin-top: 100px">
        ...
    </div>
</body>
</html>

SpringSecurity 整合 Thymeleaf

需要实现的功能:

​ 1、当用户未登录时——显示登录按钮

​ 2、当用户登录时——显示退出登录按钮

​ 3、当用户登录至页面时只显示相对应的权限链接

导入依赖

<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”

并 修改 index.html 页面

要实现此功能必须关闭csrf功能:跨站请求伪造

在 SecurityConfig 配置类的 configure(HttpSecurity http) 方法中

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    ...
        
	http.csrf().disable();
    
    // 下方无代码
}
<!DOCTYPE html>
<html lang="en" 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>
    <h1>hello SpringSecurity</h1>
    <hr>
    <!--未登录的情况-->
    <div sec:authorize="!isAuthenticated()">
        <p  style="text-align: right; margin-right: 20px">
            <a th:href="@{/login}">登录</a>
        </p>
    </div>

    <!--用户已登录-->
    <div sec:authorize="isAuthenticated()">

        <!--显示用户名和角色-->
        name:<span sec:authentication="principal.username"></span>
        role:<span sec:authentication="principal.authorities"></span>

        <!--显示退出登录按钮-->
        <p  style="text-align: right; margin-right: 20px">
            <a th:href="@{/logout}">退出登录</a>
        </p>
    </div>
    
    ...
    
</body>
</html>

实现当用户登录至页面时只显示相对应的权限链接

<!DOCTYPE html>
<html lang="en" 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>
    <h1>hello SpringSecurity</h1>
    <hr>
    <!--未登录的情况-->
    <div sec:authorize="!isAuthenticated()">
        <p  style="text-align: right; margin-right: 20px">
            <a th:href="@{/toLogin}">登录</a>
        </p>
        <div style="text-align: center; margin-top: 100px">
            <div>
                <p>
                    <a th:href="@{/level01/1}">level 01-1</a>
                    <a th:href="@{/level01/2}">level 01-2</a>
                    <a th:href="@{/level01/3}">level 01-3</a>
                </p>
            </div>
            <div>
                <p>
                    <a th:href="@{/level02/1}">level 02-1</a>
                    <a th:href="@{/level02/2}">level 02-2</a>
                    <a th:href="@{/level02/3}">level 02-3</a>
                </p>
            </div>
            <div>
                <p>
                    <a th:href="@{/level03/1}">level 03-1</a>
                    <a th:href="@{/level03/2}">level 03-2</a>
                    <a th:href="@{/level03/3}">level 03-3</a>
                </p>
            </div>
        </div>
    </div>

    <!--用户已登录-->
    <div sec:authorize="isAuthenticated()">
        <div>

            <!--显示用户名和角色-->
            name:<span sec:authentication="principal.username"></span>
            role:<span sec:authentication="principal.authorities"></span>

            <!--显示退出登录按钮-->
            <p  style="text-align: right; margin-right: 20px">
                <a th:href="@{/logout}">退出登录</a>
            </p>
        </div>
        <div style="text-align: center; margin-top: 100px">
            <div sec:authorize="hasRole('doctor')">
                <p>
                    <a th:href="@{/level01/1}">level 01-1</a>
                    <a th:href="@{/level01/2}">level 01-2</a>
                    <a th:href="@{/level01/3}">level 01-3</a>
                </p>
            </div>
            <div sec:authorize="hasRole('master')">
                <p>
                    <a th:href="@{/level02/1}">level 02-1</a>
                    <a th:href="@{/level02/2}">level 02-2</a>
                    <a th:href="@{/level02/3}">level 02-3</a>
                </p>
            </div>
            <div sec:authorize="hasRole('teacher')">
                <p>
                    <a th:href="@{/level03/1}">level 03-1</a>
                    <a th:href="@{/level03/2}">level 03-2</a>
                    <a th:href="@{/level03/3}">level 03-3</a>
                </p>
            </div>
        </div>
    </div>
</body>
</html>

记住我功能

只要登录之后,关闭浏览器,依然可以保留登录状态

原因:

​ 登录成功后,服务器会为用户生成cookie并发送给浏览器保存

​ 以后用户登录会携带此cookie,只要通过检查就可以免登录了

​ 如果点击注销,则会删除相应用户的cookie

//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
	...
   
    http.rememberMe();  // 记住我
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIz17zO3-1592456716357)(F:\项目学习\SpringBoot\springboot Security\image-20200618115148750.png)]

自定义登录页面

在views目录下创建登录页面 login.html

PS:登录表单的提交方式必须是 post

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form  th:action="@{/do/login}" style="text-align: center; margin-top: 100px" method="post">
    <h1>用户登录页面</h1>
    <hr>
    <p>username:<input type="text" name="username" placeholder="用户名"/></p>
    <p>password:<input  type="password" name="password" placeholder="密码"/></p>
    <p><input type="checkbox" name="remember"> 记住我
        <input type="submit" value="登录"></input>
    </p>
</form>
</body>
</html>

修改SecurityConfig 配置类的 configure(HttpSecurity http) 方法

protected void configure(HttpSecurity http) throws Exception {

    // 设置请求资源的权限(授权)
    http
        ...
        
        .formLogin()    // 设置未授权请求跳转的登录表单页面
        .loginPage("/toLogin").permitAll() // 指定登录页面地址且所有人都可以访问
        .usernameParameter("username") // 告诉SpringSecurity登录账号请求参数名(与表单一致)
        .passwordParameter("password") // 告诉SpringSecurity登录密码请求参数名(与表单一致)
        .loginProcessingUrl("/do/login") // 登录表单提交地址
        ;
    
		...
            
    http.rememberMe().rememberMeParameter("remember"); //开启记住我功能,参数与表单一致; // 记住我
}

控制器实现跳转

 @RequestMapping("/toLogin")
public String toLogin(){
    return "views/login";
}

SecurityConfig 配置类 全部代码

package com.jason.controller.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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @authot jason_yan
 * @date 2020/6/18-9:54
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 设置请求资源的权限(授权)
        http
                .authorizeRequests() // 对请求进行授权
                .antMatchers("/").permitAll() // 设置首页任何人都可访问
                .antMatchers("/level01/**").hasRole("doctor") // 指定要设置权限信息的地址和授权角色
                .antMatchers("/level02/**").hasRole("master") // 两个*代表目录的深度(任意深度)
                .antMatchers("/level03/**").hasRole("teacher")
                .and()
                .formLogin()    // 设置未授权请求跳转的登录表单页面
                .loginPage("/toLogin").permitAll()    // 指定登录页面地址且所有人都可以访问
                .usernameParameter("username") // 告诉SpringSecurity登录账号请求参数名(与表单一致)
                .passwordParameter("password") // 告诉SpringSecurity登录密码请求参数名(与表单一致)
                .loginProcessingUrl("/do/login") // 登录表单提交地址
                ;

        http.exceptionHandling().accessDeniedPage("/no/auth"); // 用有访问权限的角色被访问拒绝后跳转的路径

        http.logout()     //开启用户退出登录的功能
            .logoutSuccessUrl("/");    //设置成功退出登录后前往主页

        http.csrf().disable(); // 关闭跨站请求伪造

        http.rememberMe().rememberMeParameter("remember"); //开启记住我功能,参数与表单一致; // 记住我
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        // 模拟假数据
        auth.inMemoryAuthentication()
                .passwordEncoder(bCryptPasswordEncoder) // 设置加密方式
                .withUser("zs").password(bCryptPasswordEncoder.encode("111")).roles("doctor")
                .and()
                .withUser("ls").password(bCryptPasswordEncoder.encode("222")).roles("master")
                .and()
                .withUser("ww").password(bCryptPasswordEncoder.encode("333")).roles("teacher");
    }
}

SpringSecurity 整合 数据库

待续…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值