【SpringCloud】Spring Security解决跨域问题、自定义校验方法

21 篇文章 1 订阅
4 篇文章 0 订阅

学习视频: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("*")
                // 是否允许cookie
                .allowCredentials(true)
                // 设置允许的请求方式
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                // 设置允许的header属性
                .allowedHeaders("*")
                // 跨域允许时间
                .maxAge(3600);
    }
}

②开启SpringSecurity的跨域访问

在SecurityConfig实现类的configure方法中添加 http.cors();即可。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 创建 BCryptPasswordEncoder 注入容器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 认证失败处理
     */
    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    /**
     * 无权限失败处理
     */
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    /**
     * 配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // 关闭csrf
                .csrf().disable()
                // 不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();

        // 在过滤器UsernamePasswordAuthenticationFilter之前,添加我们自定义的过滤器JwtAuthenticationTokenFilter
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // 配置异常处理器
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);

        // 开启跨域访问
        http.cors();
    }

    /**
     * 用于用户认证
     * @return
     * @throws Exception
     */
    @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权限的用户可以访问。

基本原理:

  1. 先获取用户的权限的Set集合。
  2. 判断权限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();

        // 判断用户权限集合中是否存在authority
        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();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

望天边星宿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值