# 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,密码会打印在控制台
# 基本原理
# 过滤器链
FilterSecurityInterceptor
:是一个方法级的过滤器,基本位于过滤器链的最底部
ExceptionTranslationFilter
:是一个异常过滤器,用于处理在认证授权过程中抛出的异常
UsernamePasswordAuthenticationFilter
:对 /login 的 Post 请求做拦截,校验表单的用户名,密码
# 过滤器加载过程
- 通过
DelegatingFilterProxy
:执行方法 doFilter
- 在
doFilter
方法中执行 initDelegate
- 在
initDelegate
中通过 FilterChainProxy
得到所有过滤器 filters
# 两个重要接口
# UserDetailsService 接口
- 创建类继承
UsernamePasswordAuthenticationFilter
,重写三个方法
- 创建类实现
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").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(); |
| } |
| } |
# 自动登录
# 原理分析
# 功能实现
# 创建数据库表
| 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; |
# 注入数据源,配置操作数据库对象
| |
| @Autowired |
| private DataSource dataSource; |
| @Bean |
| public PersistentTokenRepository persistentTokenRepository() { |
| JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); |
| jdbcTokenRepository.setDataSource(dataSource); |
| |
| 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); |
| |
| 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").hasAnyRole("sale", "manager") |
| .anyRequest().authenticated() |
| .and().rememberMe().tokenRepository(persistentTokenRepository()) |
| .tokenValiditySeconds(60) |
| .userDetailsService(userDetailsService) |
| .and().csrf().disable(); |
| } |
| } |
# 登录页添加啊复选框
| <input type="checkbox" name="remember-me">自动登录 |