-
引入Spring Security依赖
-
当我们引入Spring Security框架之后,默认所有请求都得通过认证,没有通过的话会被重定向到内置的登录页面,登录成功就会自动重定向到此前访问的页面,但是所有的POST请求是不允许访问的,GET可以。
我们需要创建一个配置类,继承
WebSecurityConfigurerAdapter
类,重写其中的方法:他默认会调用父类的一个
super.configure(http)
方法,注释掉之后,所有的请求不需要通过认证也可以直接访问了@Override protected void configure(HttpSecurity http) throws Exception { // 如果不调用父类方法,默认所有请求都不需要通过认证,可以直接访问 // super.configure(http);
-
还需要定义一个没有通过认证也能访问的url数组,匹配的路径不需要认证也可以访问(例如登录请求就需要放行)
// 需要放行的请求 String[] urls = { "/login", "/**" };
-
POST请求在登陆成功之后不能访问,是因为在Spring Security框架中,默认开启了防止伪造的跨域攻击的机制
其基本做法就是在POST请求中,要求客户端提交其随机生成的一个UUID值,例如,(在没有禁用防止伪造跨域攻击时)在Spring Security的登录页面中有:
<input name="_csrf" type="hidden" value="b6dc65f8-e0cf-4907-bdaf-a5f19b759f93" />
以上代码中的
value
值就是一个UUID值,是前次GET请求时由服务器端响应的,服务器端会要求客户端携带此UUID来访问,否则,就会将请求视为伪造的跨域攻击行为我们需要在继承
WebSecurityConfigurerAdapter
类中将防止伪造跨域攻击的机制禁用http.csrf().disable()
,POST请求就可以访问// 将防止伪造跨域攻击的机制禁用 http.csrf().disable();
-
在Spring Security处理认证时,还会自动装配Spring容器中的密码编码器,所以还需要在配置类中添加密码编码器:
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
-
要实现前后端分离的登录模式,需要使用控制器接收来自客户端的登录请求,然后在Service调用
AuthenticationManager
的authenticate()
方法处理认证,所以需要在配置类中添加一个认证管理器并添加到Spring容器中供Service调用。@Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
-
Spring Security默认提供了用户名
user
和启动时随时生成的UUID密码,如果要自定义账号密码,需要实现UserDetailsService
接口,重写接口中的方法,Spring Security框架在处理认证时,会自动根据提交的用户名(用户在登录表单中输入的用户名)来调用重写的方法:UserDetails loadUserByUsername(String username);
以上方法应该返回匹配的用户详情(
UserDetails
类型的对象),接下来,Spring Security会自动根据用户详情(UserDetails
对象)来完成认证过程,例如判断密码是否正确等。当执行认证时会自动调用该方法,然后在方法中可以通过参数(用户输入的用户名)来查数据库
(如果查询结果为空,证明该用户不存在,抛出异常由全局异常处理器来处理)
,将查询结果中所需的账号、密码、权限赋值到UserDetails
类型的对象中返回,此对象中的账号、密码以及权限,就是自定义的账号密码,也就是后面 Spring Security判断登录的依据。 -
当客户端发送求登录时,在Service中执行认证,当认证成功后时会返回
Authentication
接口类型的对象,此类型中的principal
就是通过认证的用户信息,也就是之前自定义账号密码时重写的方法的返回值。然后需要从principal
中取出当前用户的账号、密码以及权限来生成JWT,然后返回给客户端。在此之后客户端访问服务端都需要携带登录成功时返回的JWT。
-
Spring Security框架是依据Security上下文
(SecurityContext)
中的认证信息来判定当前用户是否通过认证以及判断用户的权限,所以当客户端携带JWT发送请求时,需要在控制器之前解析JWT、创建认证对象、然后将认证对象保存到security上下文中,因此需要创建一个过滤器来处理。 -
并且,此过滤器还需在Spring Security的过滤器之前执行,还应该在
SecurityConfiguration
中添加此过滤器,并将过滤器添加到Spring Security框架的过滤器链中@Autowired private JwtAuthorizationFilter jwtAuthorizationFilter; // 将JWT过滤器添加到Spring Security框架的过滤器链中 http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
-
需要先在配置类(强烈建议
SecurityConfiguration
)上添加@EnableGlobalMethodSecurity(prePostEnabled = true)
注解,以启用方法级别的权限检查!需要注意:当客户端的异步请求定义了请求头中的
Authorization
时,在服务器端,在SecurityConfiguration
类的void configurer(HttpSecurity http)
方法中,需要添加以下配置:http.cors();
否则客户端将出现跨域错误!
-
当前后端需要跨域访问
在默认情况下,是不允许发送跨域访问请求的.
当服务器端允许来自跨域的客户端发送请求时,在Spring Boot项目中,需要使用配置类实现WebMvcConfigurer
接口,并重写addCorsMappings()
方法进行配置.public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedHeaders("*") .allowedMethods("*") .allowCredentials(true) .maxAge(3600);
完整配置类代码:
/**
* Spring Security配置类
*
* @author Hyde
* @version 0.0.1
*/
@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 如果不调用父类方法,默认所有请求都不需要通过认证,可以直接访问
// super.configure(http);
// 白名单
String[] urls = {
"/favicon.ico",
"/doc.html",
"/**/*.js",
"/**/*.css",
"/swagger-resources",
"/v2/api-docs",
"/admins/login"
};
// 启用CorsFilter(Spring Security内置的处理跨域的过滤器)
http.cors();
// 将防止伪造跨域攻击的机制禁用
http.csrf().disable();
// 提示:关于请求路径的配置,如果同一路径对应多项配置规则,以第1次配置的为准
http.authorizeRequests() // 管理请求授权
// .mvcMatchers(HttpMethod.OPTIONS, "/**")
// .permitAll()
.mvcMatchers(urls) // 匹配某些路径
.permitAll() // 直接许可,即可不需要通过认证即可访问
.anyRequest() // 除了以上配置过的以外的其它所有请求
.authenticated(); // 要求是“已经通过认证的”
// 将JWT过滤器添加到Spring Security框架的过滤器链中
http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
// 启用登录表单
// 当未认证时:
// -- 如果启用了表单,会自动重定向到登录表单
// -- 如果未启用表单,则会提示403错误
// http.formLogin();
}
@Autowired
private JwtAuthorizationFilter jwtAuthorizationFilter;
}