spring security学习笔记

spring security学习笔记

1、spring security简介

spring security的核心功能主要包括

  • 认证(你是谁)
  • 授权(你能干什么)
  • 攻击防护(防止伪造身份)

其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。

2、spring security Web权限方案

当没有设置登录系统的用户名和密码时,默认的账号时user,项目启动时,系统会给控制台发送一条密码,用这个账号加密码就可以登录系统

//系统给控制台发送的密码
Using generated security password: dae47545-7130-4fa1-aacf-aa8b8c76f40a

2.1、设置登录系统的账号密码

方式一:配置文件(application.properties)修改登录的账号和密码
# 通过配置文件的方式配置security的用户名和密码
spring.security.user.name=atguigu   #用户名
spring.security.user.password=atguigu   #密码

方式二:通过配置类

注意:在使用密码加密的时候必须得将BCryptPasswordEncoder对象加入到容器中,否则会报错

package com.atguigu.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration//声明该类是一个配置类
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //重写configure方法
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        //密码加密对象,BCrypt强哈希方法 每次加密的结果都不一样。
        BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
        //对密码进行加密
        String passwprd = passwordEncoder.encode("123");
        //设置用户名、密码、以及角色
        auth.inMemoryAuthentication().withUser("root").password(passwprd).roles("admin");
    }

    //使用了密码加密,必须得把BCryptPasswordEncoder对象添加到容器中
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

方式三:自定义编写实现类
  • 1、创建配置类,设置使用哪个userDetailsService实现类
package com.atguigu.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration//声明该类是一个配置类
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    //重写configure方法
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        //设置成自己编写的实现类
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    //使用了密码加密,必须得把BCryptPasswordEncoder对象添加到容器中
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

  • 2、编写实现类,返回User对象,User对象有用户名密码和操作权限
package com.atguigu.service;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("userDetailsService")//这里的名字要和配置类里面注入对象的名字相同
public class MyUserDetailsService implements UserDetailsService {


    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        return new User("mary",new BCryptPasswordEncoder().encode("123"),auths);
    }
}

3、查询数据库完成用户认证

整合mybatisPlus完成数据库操作

第一步:引入相关依赖

	    <!--mybitisplus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>

        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        
        <!--Lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

第二步:创建数据库表

user表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I6ifPm1j-1631511850127)(C:\Users\fei\AppData\Roaming\Typora\typora-user-images\image-20210910193046775.png)]

第三步:创建user表对应的实体类

package com.atguigu.entity;

import lombok.Data;

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

}

第四步:整合mybatisPlus,创建接口,继承mybatisPlus的接口

package com.atguigu.mapper;

import com.atguigu.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

@Repository//防止MyUserDetailsService中注入UserMapper对象报红,报红也能正常使用
public interface UserMapper extends BaseMapper<User> {
}

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

package com.atguigu.service;

import com.atguigu.mapper.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

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

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //创建一个条件构造器
        QueryWrapper<com.atguigu.entity.User> wrapper=new QueryWrapper();
        //根据用户名查询数据库  == where username=?
        wrapper.eq("username",username);
        //selectOne():得到某一条记录
        com.atguigu.entity.User user = userMapper.selectOne(wrapper);
        //判断
        if (user==null){//数据库没有用户名,认证失败
            throw new UsernameNotFoundException("用户名不存在,认证失败");
        }

        //从查询数据库返回user对象,返回用户名和密码
        List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        return new User(user.getUsername(),new BCryptPasswordEncoder().encode(user.getPassword()),auths);
    }
}

第六步:在启动类上添加MapperScan注解

package com.atguigu;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.atguigu.mapper")
public class Springbootweb1Application {

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

}

第七步:配置数据库信息

# 配置数据库连接信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/student?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456

4、自定义登录页面,不需要认证也可以访问

第一步:在配置类(SecurityConfigTest)中实现相关的配置

在SecurityConfigTest配置类中重写下面这个方法

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        //自定义自己编写的登录页面
        http.formLogin()
                .loginPage("/login.html")  //跳转到登录页面设置
                .loginProcessingUrl("/user/login") //登录访问路径
                .defaultSuccessUrl("/test/index").permitAll()  //登录成功之后 访问路径
                .and().authorizeRequests()
                .antMatchers("/","/user/login,/static/**").permitAll() //设置哪些路径可以直接访问,不需要认证
                .anyRequest().authenticated()
                .and().csrf().disable();    //关闭csrf防护
    }

第二步:创建相关页面(login.html)和Controller

注意:表单中的用户名的name必须是username,表单中的密码的name必须是password,提交方式必须是post,否则无法识别

<!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="登录">
    </form>
</body>
</html>

创建Controller

package com.atguigu.controller;

import com.atguigu.entity.User;
import com.atguigu.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class testController {

    @Autowired
    ApplicationContext applicationContext;

    @GetMapping("hello")
    public String hello(){
        return "hello security";
    }

    @GetMapping("aaa")
    public String hello1(){
        return "hello aaa";
    }

    @GetMapping("index")
    public Object index(){
        System.out.println(applicationContext.getBean("userBean").toString());
        return applicationContext.getBean("userBean");
    }
}

5、基于角色或权限进行访问控制

5.1、hasAuthority方法与hasAnyAuthority方法

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

第一步:在配置类设置当前访问地址有哪些权限
//设置当前登录用户只有具有某个权限才可以访问这个路径,例如admins权限
.antMatchers("/test/index").hasAuthority("admins")
第二步:在UserDetailsService中,设置返回User对象的权限

1、设置单个

        //创建用户权限对象,并设置对象的权限
        List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admins");

2、设置多个对象权限,之间用逗号隔开

         //设置多个用户权限,多个权限之间用逗号隔开
         .antMatchers("/test/index").hasAnyAuthority("admin,admins")

5.2、hasRole方法

如果用户具备给的角色就允许访问,否则出现403

如果当前主体具有指定的角色,则返回true

1、设置角色权限

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

2、手动添加角色权限到UserDetailsService中,注意添加角色权限必须得带上前缀 ROLE_

//创建用户权限对象,并设置对象的权限s
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_sale");

5.3、hasAnyRole

表示用户具备任何一个条件都可以访问

1、给用户添加角色,可以设置多个,用逗号隔开

//创建用户权限对象,并设置对象的权限s
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_sale,ROLE_admin");

2、设置用户权限

//hasAnyRole方法
.antMatchers("/test/index").hasAnyRole("sale")

6、自定义403页面

6.1、修改访问配置类

 //没有权限跳转到自定义403页面
 http.exceptionHandling().accessDeniedPage("/unauth.html");

7、认证授权注解的使用

7.1、@Secured

判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀 “ROLE_”

第一步:使用注解先要开启注解功能

@EnableGlobalMethodSecurity(securedEnabled = true) 注解,可以放在配置类上也可以放在启动类上

@EnableGlobalMethodSecurity(securedEnabled = true)
public class Springbootweb1Application {
第二步:在Controller的方法上使用注解,设置角色

@Secured 中可以写一个或多个权限,多个用大括号包起来,逗号分隔,且设置权限必须得带前缀 “ROLE_”

	@GetMapping("update")
    @Secured({"ROLE_role","ROLE_manager"})
    public String update(){
        return "hello update";
    }
第三步:在userDetailsService中设置用户角色
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_role");

7.2、@PreAuthorize

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

第一步:使用注解先要开启注解功能

@EnableGlobalMethodSecurity(securedEnabled = true) 注解,可以放在配置类上也可以放在启动类上

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class Springbootweb1Application {
第二步:在Controller的方法上使用注解,设置角色

登录的用户具有admins权限才能访问

    @GetMapping("a")
    @PreAuthorize("hasAnyAuthority('admins')")
    public String update1(){
        return "hello PreAuthorize";
    }
第三步:在userDetailsService中设置用户角色
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_role");

7.3、@PostAuthorize

@PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限

第一步:使用注解先要开启注解功能

@EnableGlobalMethodSecurity(securedEnabled = true) 注解,可以放在配置类上也可以放在启动类上

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class Springbootweb1Application {
第二步:在Controller的方法上使用注解,设置角色

登录的用户不具有admin权限也能进入方法,运行方法,先进方法再判断

    @GetMapping("b")
    @PostAuthorize("hasAnyAuthority('admin')")
    public String update2(){
        System.out.println("方法运行了.......");
        return "hello PreAuthorize";
    }
第三步:在userDetailsService中设置用户角色
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_role");

7.4、@PostFilter

这个注解不常用,对方法返回的数据进行过滤

使用:

    @GetMapping("c")
    @PostAuthorize("hasAnyAuthority('admins')")
    @PostFilter("filterObject.username=='admin1'")//只返回admin1的数据
    public List<User> update3(){
        List<User> list=new ArrayList<>();
        list.add(new User(11,"admin1","123"));
        list.add(new User(12,"admin2","456"));
        System.out.println(list);
        return list;
    }

7.5、@PreFilter

这个注解不常用,对传入方法的数据进行过滤

使用:

    @GetMapping("d")
    @PostAuthorize("hasAnyAuthority('admins')")
    @PreFilter(value = "filterObject.id%2==0")//只接受id能被2整除的用户传进来的数据
    public List<User> update4(@RequestBody List<User> list){
        return list;
    }

8、用户注销

在配置类中进行配置

logoutUrl:设置一个退出的地址

logoutSuccessUrl:退出之后跳转的地址

http.logout().logoutUrl("/logout").logoutSuccessUrl("/index").permitAll();

用户注销测试

第一步:修改配置类,登录成功之后跳转到成功页面
  • 修改配置类中的登录成功访问地址
.defaultSuccessUrl("/success.html").permitAll()  //登录成功之后 访问路径
第二步:在成功页面上添加超链接,写出推出路径地址

在static文件夹中新建success.html页面,也就是成功页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>这是登录成功页面</h1>
    <a href="/logout">退出</a>
</body>
</html>
第三步:登录成功之后,在成功页面点击退出,在访问其它Controller,不能进行访问

9、自动登录

9.1、自动登录原理

在这里插入图片描述

9.2、具体实现

第一步:创建数据库表

使用自动创建表,在配置类里配置

jdbcTokenRepository.setCreateTableOnStartup(true);//自动生成数据库表
第二步:配置类,注入数据源,配置操作数据库对象
//注入数据源
    @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)//设置查询数据库对象
第四步:在登录页面添加复选框

在login.html添加复选框

注意:此处name属性值必须为**“remember-me”**,不能改为其它值

   <input type="checkbox" name="remember-me">自动登录
   <br>

第三步:配置类中配置自动登录
.and().rememberMe().tokenRepository(persistentTokenRepository())//设置自动登录
.tokenValiditySeconds(60)//设置自动登录有效时长,以秒为单位
.userDetailsService(userDetailsService)//设置查询数据库对象
第四步:在登录页面添加复选框

在login.html添加复选框

注意:此处name属性值必须为**“remember-me”**,不能改为其它值

   <input type="checkbox" name="remember-me">自动登录
   <br>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值