一、Spring Security介绍
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。由于它是Spring生态系统中的一员,因此它伴随着整个Spring生态系统不断修正、升级,在spring boot项目中加入springsecurity更是十分简单,使用Spring Security 减少了为企业系统安全控制编写大量重复代码的工作。
二、快速入门
1、创建SpringBoot项目
1、依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
</parent>
<dependencies>
<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>
</dependencies>
2、配置文件:
server.port=8080
spring.application.name=spring-security
3、启动类:
@SpringBootApplication
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class,args);
}
}
4、controller:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
@RequestMapping("/success")
public String success(){
return "success";
}
@RequestMapping("/fail")
public String fail(){
return "fail";
}
}
2、测试
地址:localhost:8080/hello
初始用户:user ,密码:控制台输出。
三、认证
1、登录校验流程
SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。
- UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请求。入门案例的认证工作主要有它负责。
- ExceptionTranslationFilter:处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException。
- FilterSecurityInterceptor:负责权限校验的过滤器。
2、认证流程
- Authentication接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。
- AuthenticationManager接口:定义了认证Authentication的方法
- UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。
- UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。
3、自定义认证方式
所以根据流程图可知我们只需要实现UserDetailsService接口,就可以实现我们自己的认证
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>().eq(User::getName, username);
User user = userMapper.selectOne(queryWrapper);
//if(null == user){
if(Objects.isNull(user)){
throw new RuntimeException("用户名或密码错误");
}
LoginUser loginUser = new LoginUser(user);
//TODO:查询用户权限
//继承UserDetails类
return loginUser;
}
}
security配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
/**
*加密解密
*/
@Bean
public PasswordEncoder passwordEncoder(){
//加密器的实现器
//return new BCryptPasswordEncoder(10);
//明文密码
return NoOpPasswordEncoder.getInstance();
}
/**
* 登录时需要调用AuthenticationManager.authenticate执行一次校验
*
* @param config
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
/**
* 放行
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 禁用basic明文验证
//.httpBasic().disable()
// 前后端分离架构不需要csrf保护
.csrf().disable()
// 禁用默认登录页
//.formLogin().disable()
// 禁用默认登出页
//.logout().disable()
// 设置异常的EntryPoint,如果不设置,默认使用Http403ForbiddenEntryPoint
//.exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(invalidAuthenticationEntryPoint))
// 前后端分离是无状态的,不需要session了,直接禁用。
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
// 允许所有OPTIONS请求
//.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 允许直接访问授权登录接口
.requestMatchers(HttpMethod.POST, "/user/login").permitAll()
// 允许 SpringMVC 的默认错误地址匿名访问
//.requestMatchers("/error").permitAll()
// 其他所有接口必须有Authority信息,Authority在登录成功后的UserDetailsImpl对象中默认设置“ROLE_USER”
//.requestMatchers("/**").hasAnyAuthority("ROLE_USER")
// 允许任意请求被已登录用户访问,不检查Authority
.anyRequest().authenticated())
//.authenticationProvider(authenticationProvider())
// 加我们自定义的过滤器,替代UsernamePasswordAuthenticationFilter
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
此时就可以使用数据库中的用户名和密码进行登录校验