目录
1. 简介
Spring Security 是一个提供身份验证、授权和防护常见攻击的框架。它为确保命令式和反应式应用程序的安全提供一流的支持,是确保基于 Spring 的应用程序安全的事实标准。
1.1 身份认证
身份验证是我们验证试图访问特定资源者身份的方式。验证用户身份的常用方法是要求用户输入用户名和密码。一旦进行了身份验证,我们就知道了用户的身份,并可以执行授权。Spring Security 提供对用户身份验证的内置支持。以下是Spring Security支持的认证机制:
-
用户名 & 密码
-
OpenID Connect 的 OAuth 2.0 登录和非标准 OAuth 2.0 登录
-
SAML 2.0 登录
-
中央认证服务器 (CAS)
-
如何在会话过期后记住用户
-
JAAS 身份验证
-
OpenID 身份验证(不要与 OpenID Connect 混淆)
-
SiteMinder 或 Java EE 安全性等外部机制进行身份验证
-
X509 认证
我们工作中用的最多的也就是"用户名 & 密码" 与 "OAuth2";所以,本篇文章就以用户名&密码讲解。
1.2 授权
无论你选择以何种方式进行身份验证--是使用 Spring Security 提供的机制和提供商,还是与容器或其他非 Spring Security 身份验证机构集成--你都会发现授权服务可以以一致而简单的方式在你的应用程序中使用。
2. 实战案例
2.1 依赖管理
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
当环境中引入上面的依赖后,默认情况会对所有的请求都进行拦截,同时启动服务时会输出随机密码,而用户则默认是"user"。
2.2 自定义用户
在配置文件中配置用户名密码等信息。
spring:
security:
user:
name: pack
password: 123456
当你在配置文件中做了如上配置后,控制台将不会输出随机生成的密码。
2.3 自定义授权规则
在上面的示例中,默认情况Spring Security对所有的请求进行拦截,有些时候我们希望只对某些请求进行拦截、静态资源自动放行,这时候就需要我们自定义授权规则。在Spring Security中是通过配置SecurityFilterChain Bean对象进行授权规则的配置。
@Bean
public SecurityFilterChain apiSecurity(HttpSecurity httpSecurity) throws Exception {
// 关闭csrf
httpSecurity.csrf(csrf -> csrf.disable()) ;
// 拦截所有以/api/开头的请求
httpSecurity.authorizeHttpRequests().antMatchers("/api/**").authenticated() ;
// 放行所有的静态资源(其实,如果你这里就配置了一个/api/**,那么下面的都不用配置,因为只会拦截/api/)
httpSecurity.authorizeHttpRequests().antMatchers("*.js", "*.css", "*.html").permitAll() ;
// 而这配置,则会通过表单形式进行用户名&密码的登录授权操作。默认提供了简单的登录界面
httpSecurity.formLogin(withDefaults()) ;
return httpSecurity.build() ;
}
默认登录页
针对不同的请求配置不同的权限(角色)。
// 访问/api/前缀的请求用户必须具备MGR角色
httpSecurity.authorizeHttpRequests().antMatchers("/api/**").hasAnyRole("MGR") ;
// /admin/前缀的请求必须具备SUPER角色
httpSecurity.authorizeHttpRequests().antMatchers("/admin/**").hasAnyRole("SUPER") ;
配置文件中配置角色
spring:
security:
user:
roles:
- SUPER
当你不具备对应的角色,你将看到如下错误
2.4 自定义用户查询
在上面的示例中,都是基于配置文件进行用户的配置角色的设置,都是静态的信息,而实际工作中,都是需要从数据库中进行查询的。关于数据库的操作,我这里使用的spring data jpa。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
实体User定义
@Entity
@Table(name = "p_user")
public class User implements UserDetails {
private static final long serialVersionUID = 1L ;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
private String username ;
private String password ;
private String email ;
private String address ;
private Integer age ;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 这里为了简单起见,就直接固定了
return Arrays.asList(
new SimpleGrantedAuthority("ROLE_MGR"),
new SimpleGrantedAuthority("ROLE_NORMAL")
);
}
@Override
public String getPassword() {
return this.password ;
}
@Override
public String getUsername() {
return this.username ;
}
// 下面几个方法你,也应该根据你实际系统用户的状态设置
@Override
public boolean isAccountNonExpired() {
return true ;
}
@Override
public boolean isAccountNonLocked() {
return true ;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true ;
}
// getter, setter
}
Spring Security中如果你需要基于数据库的验证,那么你还需要提供如下的配置Bean
@Bean
UserDetailsService packUserDetailsService(UserRepository userRepository) {
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username) ;
return user ;
}
} ;
}
@Bean
PasswordEncoder packPasswordEncoder() {
// 生产环境中你不应该使用该种PasswordEncoder。
return NoOpPasswordEncoder.getInstance() ;
}
UserRepository接口
public interface UserRepository extends JpaRepository<User, Long> {
// 我这是由于有很多的重复数据,所以。。。
@Query(value = "select * from p_user u where u.username = ?1 limit 1", nativeQuery = true)
User findByUsername(String username) ;
}
通过以上的配置就完成了与数据库的结合登录验证。
2.5 自定义登录页面
默认情况下的,登录页面你肯定不会应用到生产环境中,都会自定义登录页面。在如下目录新增自定义的登录页面
配置
@Bean
SecurityFilterChain apiSecurity(HttpSecurity httpSecurity) throws Exception {
// ...
httpSecurity.formLogin(login -> login.loginPage("/login.html").loginProcessingUrl("/login")) ;
return httpSecurity.build() ;
}
以上是本篇文章的全部内容,如对你有帮助就请作者吃个棒棒糖🍭。
完毕!!!
推荐文章
使用RabbitMQ消息队列和Redis缓存优化Spring Boot秒杀功能
Spring Boot整合Elasticsearch
Spring Boot与RabbitMQ整合:实现高可用消息队列服务
Spring Boot携手OAuth2.0,轻松实现微信扫码登录!
快速上手Spring Boot与Mybatis Plus集成
《深度解析:Redis缓存穿透、击穿与雪崩的区别及应对策略》
权威解析Spring框架九大核心功能(续篇):专业深度,不容错过