# SpringSecurity 介绍

# 框架概述

主要应用领域包括两个方面,分别是 == 用户认证(Authentication)用户授权(Authonization)== 两个部分

# 入门案例

# 导入依赖

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

# 编写 Controller

@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping("/hello")
    public String hello() {
        return "hello security";
    }
}

# 访问自动进行验证

访问时默认用户名是 user,密码会打印在控制台

# 基本原理

# 过滤器链

  1. FilterSecurityInterceptor :是一个方法级的过滤器,基本位于过滤器链的最底部
  2. ExceptionTranslationFilter :是一个异常过滤器,用于处理在认证授权过程中抛出的异常
  3. UsernamePasswordAuthenticationFilter :对 /login 的 Post 请求做拦截,校验表单的用户名,密码

# 过滤器加载过程

  1. 通过 DelegatingFilterProxy :执行方法 doFilter
  2. doFilter 方法中执行 initDelegate
  3. initDelegate 中通过 FilterChainProxy 得到所有过滤器 filters

# 两个重要接口

# UserDetailsService 接口

  1. 创建类继承 UsernamePasswordAuthenticationFilter ,重写三个方法
  2. 创建类实现 UserDetailService ,编写查询数据过程,返回 User 对象,这个 User 是安全框架提供的对象

# PasswordEncoder 接口

数据加密接口,用于返回 User 对象里面密码加密

# 设置用户名密码

# 通过配置文件

spring:
  security:
    user:
      name: baozi
      password: 123

# 通过配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123");
        auth.inMemoryAuthentication().withUser("baozi").password(password).roles("admin");
    }
    @Bean
    BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

# 自定义编写实现类

# 创建配置类,设置 userDetailsService 实现类

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
}

# 编写实现类,返回 User 对象

@Service
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        Users user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_sale");
        return new User(user.getUsername(), new BCryptPasswordEncoder().encode(user.getPassword()), auths);
    }
}

# 查询数据库完成认证

# 导入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.31</version>
</dependency>

# 配置数据源

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root

# 创建 Users 实体

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

# 创建 UserMapper 接口

@Mapper
public interface UserMapper extends BaseMapper<Users> {
}

# 在 UserService 调用 mapper 方法查询

@Service
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        Users user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_sale");
        return new User(user.getUsername(), new BCryptPasswordEncoder().encode(user.getPassword()), auths);
    }
}

# 自定义登录页

# 在配置类实现相关配置

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/success.html").permitAll()
                    .and().authorizeRequests()
                        .antMatchers("/", "/test/hello", "/user/login").permitAll()
                        .anyRequest().authenticated()
                    .and().csrf().disable();
    }
}

# 编写登录页面

在 static 目录下编写 login.html

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

# 基于权限访问控制

# hasAuthority () 方法

# 配置类设置有哪些权限

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/success.html").permitAll()
                    .and().authorizeRequests()
                        .antMatchers("/", "/test/hello", "/user/login").permitAll()
                        .antMatchers("/test/index").hasAuthority("admin")
                        .anyRequest().authenticated()
                    .and().csrf().disable();
    }
}

# 在 UserDetailsService,返回 User 对象设置权限

@Service
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        Users user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        return new User(user.getUsername(), new BCryptPasswordEncoder().encode(user.getPassword()), auths);
    }
}

# hasAnyAuthority () 方法

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/success.html").permitAll()
                    .and().authorizeRequests()
                        .antMatchers("/", "/test/hello", "/user/login").permitAll()
                        .antMatchers("/test/index").hasAnyAuthority("admin", "manager")	// 设置多个访问权限
                        .anyRequest().authenticated()
                    .and().csrf().disable();
    }
}

# hasRole () 方法

# 配置类设置有哪些角色

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/success.html").permitAll()
                    .and().authorizeRequests()
                        .antMatchers("/", "/test/hello", "/user/login").permitAll()
                        .antMatchers("/test/index").hasRole("sale")
                        .anyRequest().authenticated()
                    .and().csrf().disable();
    }
}

# 在 UserDetailsService,返回 User 对象设置权限

@Service
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        Users user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_sale");
        return new User(user.getUsername(), new BCryptPasswordEncoder().encode(user.getPassword()), auths);
    }
}

# hasAnyRole () 方法

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/success.html").permitAll()
                    .and().authorizeRequests()
                        .antMatchers("/", "/test/hello", "/user/login").permitAll()
                        .antMatchers("/test/index").hasAnyRole("sale", "manager")
                        .anyRequest().authenticated()
                    .and().csrf().disable();
    }
}

# 自定义 403 页面

# 创建 403 页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>403页面</title>
</head>
<body>
<h1>没有权限访问</h1>
</body>
</html>

# 配置没有权限访问跳转页面

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling().accessDeniedPage("/unauth.html");
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/success.html").permitAll()
                    .and().authorizeRequests()
                        .antMatchers("/", "/test/hello", "/user/login").permitAll()
//                        .antMatchers("/test/index").hasAnyAuthority("admin","manager")
                        .antMatchers("/test/index").hasAnyRole("sale", "manager")
                        .anyRequest().authenticated()
                    .and().csrf().disable();
    }
}

# 注解使用

# 开启注解功能

@SpringBootApplication
@MapperScan("cn.bjh.mapper")
@EnableGlobalMethodSecurity(securedEnabled = true)	// 开启注解功能
public class Springsecurity01QuickstartApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springsecurity01QuickstartApplication.class, args);
    }
}

# 在 controller 的方法上面使用注解

@RestController
@RequestMapping("/test")
public class TestController {
    @Secured({"ROLE_sale", "ROLE_manager"})
    @GetMapping("/update")
    public String update() {
        return "hello update";
    }
}

# 在 userDetailsService 设置用户角色

@Service
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        Users user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_sale");
        return new User(user.getUsername(), new BCryptPasswordEncoder().encode(user.getPassword()), auths);
    }
}

# 用户注销

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/index").permitAll();
        http.exceptionHandling().accessDeniedPage("/unauth.html");
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/success.html").permitAll()
                    .and().authorizeRequests()
                        .antMatchers("/", "/test/hello", "/user/login").permitAll()
                        .antMatchers("/test/index").hasAnyRole("sale", "manager")
                        .anyRequest().authenticated()
                    .and().csrf().disable();
    }
}

# 自动登录

# 原理分析

image-20231017104029357

# 功能实现

# 创建数据库表

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 CHAR SET = utf8;

# 注入数据源,配置操作数据库对象

// 在 Security 配置类中
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
    JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
    jdbcTokenRepository.setDataSource(dataSource);
    //        jdbcTokenRepository.setCreateTableOnStartup(true);
    return jdbcTokenRepository;
}

# 配置类配置自动登录

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private DataSource dataSource;
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
//        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    BCryptPasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/index").permitAll();
        http.exceptionHandling().accessDeniedPage("/unauth.html");
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/success.html").permitAll()
                    .and().authorizeRequests()
                        .antMatchers("/", "/test/hello", "/user/login").permitAll()
//                        .antMatchers("/test/index").hasAnyAuthority("admin","manager")
                        .antMatchers("/test/index").hasAnyRole("sale", "manager")
                        .anyRequest().authenticated()
                        .and().rememberMe().tokenRepository(persistentTokenRepository())
                        .tokenValiditySeconds(60)
                        .userDetailsService(userDetailsService)
                    .and().csrf().disable();
    }
}

# 登录页添加啊复选框

<input type="checkbox" name="remember-me">自动登录
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Baozi 微信支付

微信支付

Baozi 支付宝

支付宝

Baozi 微信

微信