SpringSecurity学习笔记

尚硅谷笔记详解跳转

简介

在web开发中安全是第一位!
做网站:安全应该在设计之初就考虑到
参考文档手册:https://www.springcloud.cc/spring-security-zhcn.html#what-is-acegi-security
常见的框架:shiro、SpringSecurity:很像~除了类不一样,名字不一样;
作用:认证、授权(vip1,vip2,vip3)【与SpringBoot可以无缝集成】
作用范围:

  • 功能权限
  • 访问权限
  • 菜单权限
  • …拦截器、过滤器:解决大量的原生代码~冗余
    请添加图片描述

使用(用户验证和授权)

导入SpringSecurity依赖

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

固定架构(认证):

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

固定架构(授权):

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }
}

简单实现:(认证不是从数据库授权!!!以下代码只用于理解,等会儿再展示从数据库认证)

package com.ldx.config;

import com.sun.org.apache.xpath.internal.operations.And;
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.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;


//AOP效果
//比拦截器更强大
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //链式编程

    //认证,SpringBoot 2.1.x 可以直接使用~
    //密码编码:PasswordEncoder
    //在Spring Security 5.0+ 新增了很多的加密方法~
    //passwordEncoder(new BCryptPasswordEncoder())用于密码加密
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //从内存中读
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("a").password(new BCryptPasswordEncoder().encode("1")).roles("vip2","vip3")//roles可以写多个
                .and()
                .withUser("b").password(new BCryptPasswordEncoder().encode("2")).roles("vip1","vip2","vip3")
                .and()
                .withUser("c").password(new BCryptPasswordEncoder().encode("3")).roles("vip1");//通过and可以给不同的用户设置不同的权限
        //这些数据正常应该从数据库中读
    }

    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
       //需求一、首页所有人可以访问,功能页只有对应有权限的人才能访问
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("资源名1").hasRole("vip1")
                .antMatchers("资源名2").hasRole("vip2")
                .antMatchers("资源名3").hasRole("vip3");

        //没有权限默认回到登入页面
        http.formLogin();
    }
}

注销

        http.logout().logoutUrl("/logout");//括号里指定注销的路径,与a标签href路径一致,
        // http.logout().logoutSuccessUrl("/");也可以指定注销成功后的页面

在这里插入图片描述

Thymeleaf整合SpringSecurity

第一步:引入整合包:

<!--SpringSecurity与thymeleaf整合依赖-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>

第二步引入命名空间:(老地方引入)

xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"

判断是否认证、获取角色和判断是否拥有角色

<!--判断认证-->
<!--未认证-->
<div sec:authorize="!isAuthenticated()">显示未认证内容</div>
<!--已认证-->
<div sec:authorize="isAuthenticated()">显示认证内容</div>

<!--获取用户和角色-->
<!--获取用户-->
<span sec:authentication="name"></span>
<!--获取角色-->
<span sec:authentication="principal.authorities"></span>
<!--判断是否拥有角色-->
<div sec:authorize="hasRole('vip1')"></div>

尚硅谷笔记!!!

在这里插入图片描述

1. SpringSecurity 框架简介

1.1 概要
Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的
成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方
案。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、入门案例:(需要实操)

第一步:创建SpringBoot工程

在这里插入图片描述
老规矩勾选web模块和security模块
在这里插入图片描述
然后快速创建即可
第二步:引入相关依赖

这里我建议查看一下依赖是否完整:(当时没有跳转到默认页面是因为我依赖中没有自动导入spring-boot-starter-web这个依赖)

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

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

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

第三步:编写controller进行测试
建立controller包,编写TestController 类进行测试:

@RestController
@RequestMapping("test")
public class TestController {

    @GetMapping("hello")
    public String hello(){

        return "hello security";
    }
}

测试结果:输入访问地址:http://localhost:8080/test/hello
在这里插入图片描述
进入默认页面
错误输入账号密码:
在这里插入图片描述
正确输入账号密码:账号默认:user,密码每次动态生成:
在这里插入图片描述
进入成功:
在这里插入图片描述
只要你不退出浏览器(一次会话),就可以免登入直接多次访问,只有退出浏览器才会要求重新登入
项目结构:(暂时只需要我勾出来的部分进行入门测试)
在这里插入图片描述
在这里插入图片描述

通过上面的例子可以看出,SpringSecurity底层是通过动态代理实现的,其思想是AOP

3、SpringSecurity基本原理(不需要实操,需要了解)

在这里插入图片描述
代码底层流程:重点看三个过滤器:
FilterSecurityInterceptor:是一个方法级的权限过滤器, 基本位于过滤链的最底部。
在这里插入图片描述
在这里插入图片描述
UserDetailsService 接口讲解
当什么也没有配置的时候,账号和密码是由 Spring Security 定义生成的。而在实际项目中
账号和密码都是从数据库中查询出来的。 所以我们要通过自定义逻辑控制认证逻辑。
如果需要自定义逻辑时,只需要实现 UserDetailsService 接口即可。接口定义如下:
在这里插入图片描述
UserDetailService接口:查询数据库用户名和密码过程

  • 创建类继承UsernamePasswordAuthenticationFilter,重写三个方法
  • 创建类实现UserDetailService,编写查询数据过程,返回User对象,这个User对象是安全框架提供对象
    在这里插入图片描述
    在这里插入图片描述
    PasswordEncoder 接口讲解(数据加密接口,用于对返回的User对象的密码加密)

在这里插入图片描述
在这里插入图片描述
查用方法演示(可以自己试试)

@Test
public void test01(){
// 创建密码解析器
BCryptPasswordEncoder bCryptPasswordEncoder = new 
BCryptPasswordEncoder();
// 对密码进行加密
String atguigu = bCryptPasswordEncoder.encode("atguigu");
// 打印加密之后的数据
System.out.println("加密之后数据:\t"+atguigu);
//判断原字符加密后和加密之前是否匹配
boolean result = bCryptPasswordEncoder.matches("atguigu", atguigu);
// 打印比较结果
System.out.println("比较结果:\t"+result);
}

SpringBoot 对 Security 的自动配置
https://docs.spring.io/spring-security/site/docs/5.3.4.RELEASE/reference/html5/#servlet-hello

4、SpringSecurity Web 权限方案(需要实操)

方案概括:(1)认证 ,(2)授权
在入门案例中用户名和密码都是系统提供的,而我们可以修改默认的用户名和登入密码
第一种方式:通过配置文件
第二种方式:通过配置类
第三种方式:自定义编写实体类(实际开发中连接数据库的方式,必须掌握)

方式一:通过配置文件
可以使用application.properties或application.yml文件配置,这里我使用yaml:

spring:
  security:
    user:
      name: ldx
      password: 123

测试结果:
在这里插入图片描述
方式二:编写类实现接口
注释方式一的yml设置
建立config包,编写SecurityConfig 类继承WebSecurityConfigurerAdapter 重写方法
密码必须加密!!!

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("222");
        //上面代码先进行对密码的加密,再设置用户名和密码
        auth.inMemoryAuthentication().withUser("ldx").password(password).roles("");//roles为角色
    }

   @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();//将PasswordEncoder对象在spring容器中注册,重要步骤
    }
}

测试结果:
在这里插入图片描述
方式三:自定义实现类设置
注释掉方式一二。

  • 创建配置类,设置使用哪个UserDetailService实现类

在config包下建立SecurityConfigTest继承WebSecurityConfigurerAdapter

@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();//将PasswordEncoder对象在spring容器中注册,重要步骤
    }
}
  • 编写实现类实现UserDetailsService 接口,返回User对象,User对象里有用户名,密码和权限
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        return new User("ldx",new BCryptPasswordEncoder().encode("123"),auths);

    }
}

测试结果:
在这里插入图片描述

5、连接数据库完成认证(必须掌握的重点实操)

整合MybatisPlus完成数据库操作(不仅仅使用MybatisPlus,用Mybatis或JDBC即可)
第一步:引入相关依赖

	    <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>

第二步:创建简单数据库和表如下例(入门不需要多创建表)

create table users
(
	id int auto_increment primary key,
	username varchar(20) not null,
	password varchar(20) not null
);

插入数据:
在这里插入图片描述
在这里插入图片描述
第三步:创建users表对应实体类

@Data
public class Users {
    private Integer id;
    private String username;
    private String password;
}

第四步:建立mapper包,整合mybatis-plus,创建UsersMapper 接口,继承mybatis-plus的接口

@Repository
public interface UsersMapper extends BaseMapper<Users> {

}

第五步:在MyUserDetailService中调用mapper里面的方法查询数据库进行用户认证

@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UsersMapper usersMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<Users> wrapper=new QueryWrapper<>();
        wrapper.eq("username",username);
        Users users = usersMapper.selectOne(wrapper);
        if(users==null){//数据库没有用户名,认证失败
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("role");//权限集合,实际开发是数据库中查询
        //从查询数据库返回users对象,得到用户名和密码,返回
        return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
    }
}

第六步:在启动类添加注解MapperScan来扫描到mapper注入

@SpringBootApplication
@MapperScan("com.ldx.security.mapper")
public class SecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class, args);
    }

}

第七步:在yaml文件中配置数据库信息:

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&character=UTF-8&serverTimezone=GMT%2B8
    username: root
    password: 198810

项目结构如下:
在这里插入图片描述
测试运行
在这里插入图片描述
成功:
在这里插入图片描述

6、自定义用户登入界面

在配置类(这里是SecurityConfigTest)中实现相关的配置

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()  //自定义自己编写的登入页面
            .loginPage("/login.html") //登入页面设置
            .loginProcessingUrl("/user/login") //登入的路径【必须与表单提交路径一致,且必不可少】(可以看作一个中转站,前台界面提交表单之后跳转到这个路径进行UserDetailService的验证,简单来说就是做前后端分离的)
            .defaultSuccessUrl("/test/index").permitAll()  //未登入的情况下显示我们自定义的登入界面,登入成功之后,跳转路径,进入该控制器中
            .and().authorizeRequests()
                .antMatchers("/","/test/hello","user/login").permitAll()//设置哪些路径可以直接访问,不需要认证
            .anyRequest().authenticated()
                .and().csrf().disable();//关闭csrf防护
    }

在这里插入图片描述
创建相关页面和controller
在resource里建立static文件夹,写上我们的自定义登入页面login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/user/login" method="post">
    用户名: <input type="text" name="username"><br>
    密 码: <input type="password" name="password"><br>
    <input type="submit" value="login">
</form>
</body>
</html>

在TestController里追加配置:
在这里插入图片描述

    @GetMapping("index")
    public String index(){

        return "hello index";
    }

测试路径一:http://localhost:8080/test/hello
在这里插入图片描述
测试路径二:http://localhost:8080/test/index
毫无意外的进入验证页面:
在这里插入图片描述
验证成功再重新进入该地址,且浏览器关闭之前都可以不用验证访问
在这里插入图片描述
底层还是动态代理,思想仍然是AOP

7、基于角色和权限进行访问控制

1、hasAuthority 方法
如果当前的主体具有指定的权限,则返回 true,否则返回 false

第一步:在配置类设置当前访问地址有哪些权限
在这里插入图片描述

.antMatchers("/test/index").hasAuthority("admin")//当前登入的用户,只有具有admin权限才可以访问这个路径

第二步:在UserDetailService,把返回的User对象设置权限
在这里插入图片描述

List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admin");

测试:
把权限改为abc
在这里插入图片描述
在这里插入图片描述
权限改回admin:
在这里插入图片描述
在这里插入图片描述
权限认证成功

2、hasAnyAuthority 方法(多个权限的用户访问)
如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回
true.
在这里插入图片描述

.antMatchers("/test/index").hasAnyAuthority("admin,manager")//支持多个权限用户访问该路径

具有admin测试访问:
在这里插入图片描述

8、基于角色访问控制

1、hasRole 方法
如果用户具备给定角色就允许访问,否则出现 403。
如果当前主体具有指定的角色,则返回 true。
在这里插入图片描述

 .antMatchers("/test/index").hasRole("sale")

在这里插入图片描述

List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_sale");

注意点在我们设置的用户角色名不是sale而是ROLE_sale,代码底层要求加上ROLE_
在这里插入图片描述
测试:
在这里插入图片描述
2、hasAnyRole方法
表示用户具备任何一个条件都可以访问。
在这里插入图片描述
在这里插入图片描述

9、自定义403页面(可实操)

自定义403没有权限访问的页面

在配置类中配置即可,在static包下建立unauth.html页面
在这里插入图片描述
在这里插入图片描述

 		//配置没有权限访问跳转自定义页面
        http.exceptionHandling().accessDeniedPage("/unauth.html");

测试访问:
改一下角色权限
在这里插入图片描述
在这里插入图片描述

10、注解使用(需要实操)

还原上次测试!
认证授权注解使用:

10.1、@Secured
判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。
第一步:使用注解先要开启注解功能!(启动类或配置类上)
@EnableGlobalMethodSecurity(securedEnabled=true)
在这里插入图片描述
第二步:在controller的方法上面使用注解(这里新建一个方法),设置角色

    @GetMapping("update")
    @Secured({"ROLE_sale","ROLE_manager"})
    public String update(){

        return "hello update";
    }

在这里插入图片描述
第四步,在UserDetailService类中给用户设置权限:
在这里插入图片描述
测试:(仍要登入获取权限哦)访问地址:http://localhost:8080/test/update
在这里插入图片描述
登入后:
在这里插入图片描述

10.2、@PreAuthorize
@PreAuthorize:注解适合进入方法前的权限验证, @PreAuthorize 可以将登录用
户的 roles/permissions 参数传到方法中。

第一步:先开启注解功能:
@EnableGlobalMethodSecurity(prePostEnabled = true)
第二步:还是在controller类的方法中使用,这里还是沿用update
在这里插入图片描述

    @GetMapping("update")
    @PreAuthorize("hasAnyAuthority('admin')")
    public String update(){

        return "hello update";
    }

测试:http://localhost:8080/test/update
在这里插入图片描述
第一个注解是角色认证,第二个是权限认证

10.3、@PostAuthorize
先开启注解功能:
@EnableGlobalMethodSecurity(prePostEnabled = true)
@PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值
的权限.
步骤上同!

    @GetMapping("update")
    @PostAuthorize("hasAnyAuthority('admins')")//这里权限是admins,我们并没有这个权限
    public String update(){
        System.out.println("update------");
        return "hello update";
    }

}

测试:http://localhost:8080/test/update
在这里插入图片描述
没有权限仍然能运行方法
10.4、@PostFilter(较少使用)
@PostFilter :权限验证之后对数据进行过滤 留下用户名是 admin1 的数据
表达式中的 filterObject 引用的是方法返回值 List 中的某一个元素
在这里插入图片描述
用户权限是admin1返回666,admin2返回888

10.5、@PreFilter(较少使用)
@PreFilter: 进入控制器之前对数据进行过滤
在这里插入图片描述

11、用户注销(需要实操)

不保留登入信息

第一步:在配置类中添加退出映射地址
在这里插入图片描述

//注销
http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();

第二步:在登录页面添加一个退出连接
1.修改配置类,登录成功之后跳转到成功页面
在这里插入图片描述

2.在成功页面添加超链接,设置退出路径
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    登入成功
    <a href="/logout">退出</a>
</body>
</html>

3.条件:登入成功之后,在成功页面点击退出
之后再去访问其他controller是不能进行访问的
测试:访问路径 http://localhost:8080/login.html

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
访问其他路径:需要登入
在这里插入图片描述

12、自动登入

基于数据库的记住我功能
1、原来是用cookie完成的,但它会保存用户信息在浏览器中,信息是不安全的

2、现在我们用安全框架机制实现自动登入(对cookie数据加密实现)

请添加图片描述

框架帮我做了很多事情,而我们只需要简单的配置就好了。底层流程:
请添加图片描述

具体实现

第一步:创建数据库表(我们不手建,其底层也会帮我们建的)

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;

在这里插入图片描述
第二步:配置类,注入数据源,配置操作数据库对象(配置类重新写一个或者利用之前的也行)
在这里插入图片描述

 	//注入数据源
 	@Autowired
    private DataSource dataSource;
    //配置对象
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){

        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        /*jdbcTokenRepository.setCreateTableOnStartup(true);如果没有创建表这句话加上底层会帮我们创建表*/
        return jdbcTokenRepository;
    }

第三步:配置类自动自动登入
在这里插入图片描述

.and().rememberMe().tokenRepository(persistentTokenRepository())//操作数据库的对象
.tokenValiditySeconds(60)//设置有效时长单位是秒
.userDetailsService(userDetailsService)//用到查询数据库的服务层类

第四步:在登入页面添加复选框(与上面一样,其属性名固定为remember-me)
在这里插入图片描述
测试:访问地址:http://localhost:8080/success.html发现需要登入
在这里插入图片描述
在这里插入图片描述
点进去看看:
在这里插入图片描述
点击自动登入功能之后关闭浏览器,再次访问http://localhost:8080/success.html发现访问此资源不需要再次登入了
在这里插入图片描述

13、CSRF理解

在这里插入图片描述
加上这句话:

.and().csrf().disable();//关闭csrf防护

到此为止,SpringSecurity的入门以及基本使用就结束了!!!

14、SpringSecurity 微服务权限方案(未完待续)

什么是微服务
在这里插入图片描述
微服务认证与授权实现过程
1、认证授权过程分析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
未完待续吧。。。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CharmDeer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值