学习视频:https://www.bilibili.com/video/BV1mm4y1X7Hc?p=1
🔶 系列笔记
(1)【SpringCloud】Spring Security简介、快速入门、原理流程 (2)【SpringCloud】Spring Security实现登录认证的思路与具体实现过程 (3)【SpringCloud】Spring Security授权实现流程、自定义失败处理方法 (4)【SpringCloud】Spring Security解决跨域问题、自定义校验方法
一、跨域问题解决
1.1 介绍
浏览器出于安全的考虑,使用 XMLHttpRequest对象发起 HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。 同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。
前后端分离项目,前端项目和后端项目一般都不是同源的,所以肯定会存在跨域请求的问题。
所以我们就要处理一下,让前端能进行跨域请求。
1.2 实现
① 先对SpringBoot配置,运行跨域请求。
添加配置类,实现WebMvcConfigurer 接口,config.CorsConfig
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.allowedHeaders("*")
.maxAge(3600);
}
}
②开启SpringSecurity的跨域访问
在SecurityConfig实现类的configure方法中添加 http.cors(); 即可。
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private AccessDeniedHandler accessDeniedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/user/login").anonymous()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
http.cors();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
1.3 测试
因为Postman不存在跨域,所以使用前端项目进去请求。
打开视频提供的前端项目,安装依赖并运行。
修改后端项目端口号为8888,运行前端项目并登录。 token存储在这里 测试接口
@RequestMapping("/testCors")
public ResponseResult testCors() {
return new ResponseResult(200, "testCors");
}
二、其他校验权限方法
2.1 hasAuthority
@RequestMapping("hello")
@PreAuthorize("hasAuthority('system:dept:list')")
public String hello() {
return "hello";
}
hasAuthority('system:dept:list')
作用:带有system:dept:list 权限的用户可以访问。
基本原理:
- 先获取用户的权限的Set集合。
- 判断权限
system:dept:list 是否在当前用户权限的Set集合中。
2.2 hasAnyAuthority
可以传入多个权限参数,只要带有任意一个权限即可。 hasAuthority其实就是调用的hasAnyAuthority方法。
2.3 hasRole
hasRole('system:dept:list')
会使用ROLE_ 前缀进行拼接,拼接成ROLE_system:dept:list ,所以用户必须带有这个权限才可以访问。
2.4 hasAnyRole
同理,可以传入多个权限参数,只要带有任意一个权限即可。 hasRole其实就是调用的hasAnyRole方法。
2.5 自定义方法校验
hasAuthority是最常用的,hasAnyAuthority也可以。hasRole、hasAnyRole不常用。
自定义校验规则
expression.MySecurityExpression
@Component("ex")
public class MySecurityExpression {
public boolean hasAuthority(String authority) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
List<String> permissions = loginUser.getPermissions();
return permissions.contains(authority);
}
}
在SPEL表达式中使用 @ex相当于获取容器中bean的名字未ex的对象。然后再调用这个对象的hasAuthority方法
@RequestMapping("hello2")
@PreAuthorize("@ex.hasAuthority('system:dept:list')")
public String hello2() {
return "hello";
}
2.6 基于配置的权限控制
在前面我们曾经使用过基于配置的权限控制,/user/login 接口可以匿名登录。
同样,我们可以这样设置,
.antMatchers("/hello").hasAuthority("system:dept:list")
以替代这个@PreAuthorize("hasAuthority('system:dept:list')") 注解。
@RequestMapping("hello")
@PreAuthorize("hasAuthority('system:dept:list')")
public String hello() {
return "hello";
}
三、认证成功处理器
注意:要新建一个项目,引入SpringSecurity依赖。
实际上在UsernamePasswordAuthenticationFilter进行登录认证的时候,如果登录成功了是会调用AuthenticationSuccessHandler的方法进行认证成功后的处理的。AuthenticationSuccessHandler就是登录成功处理器。
我们也可以自己去自定义成功处理器进行成功后的相应处理。
@Component
public class SuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("认证成功了");
}
}
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationSuccessHandler successHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().successHandler(successHandler);
http.authorizeRequests().anyRequest().authenticated();
}
}
四、认证失败处理器
实际上在UsernamePasswordAuthenticationFilter进行登录认证的时候,如果认证失败了是会调用AuthenticationFailureHandler的方法进行认证失败后的处理的。AuthenticationFailureHandler就是登录失败处理器。
我们也可以自己去自定义失败处理器进行失败后的相应处理。
@Component
public class FailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.println("认证失败了");
}
}
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationSuccessHandler successHandler;
@Autowired
private AuthenticationFailureHandler failureHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler);
http.authorizeRequests().anyRequest().authenticated();
}
}
五、登出成功处理器
@Component
public class SGLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("注销成功");
}
}
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationSuccessHandler successHandler;
@Autowired
private AuthenticationFailureHandler failureHandler;
@Autowired
private LogoutSuccessHandler logoutSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler);
http.logout()
.logoutSuccessHandler(logoutSuccessHandler);
http.authorizeRequests().anyRequest().authenticated();
}
}
|