Spring Security 多过滤链的使用详解

目录
  • 一、背景
  • 二、需求
    • 1、给客户端使用的api
    • 2、给网站使用的api
  • 三、实现方案
    • 方案一:
    • 方案二
  • 四、实现
    • 1、app 端 Spring Security 的配置
  • 五、实现效果
    • 1、app 有权限访问 api
    • 2、app 无权限访问 api
    • 3、admin 用户有权限访问 网站 api
    • 4、dev 用户无权限访问 网站 api
  • 六、完整代码

一、背景

在我们实际的开发过程中,有些时候可能存在这么一些情况,某些api 比如: /api/** 这些是给App端使用的,数据的返回都是以JSON的格式返回,且这些API的认证方式都是使用的TOKEN进行认证。而除了 /api/** 这些API之外,都是给网页端使用的,需要使用表单认证,给前端返回的
都是某个页面。

二、需求

1、给客户端使用的api

  • 拦截 /api/**所有的请求。
  • /api/**的所有请求都需要ROLE_ADMIN的角色。
  • 从请求头中获取 token,只要获取到token的值,就认为认证成功,并赋予ROLE_ADMIN到角色。
  • 如果没有权限,则给前端返回JSON对象 {message:"您无权限访问"}
  • 访问 /api/userInfo端点
    • 请求头携带 token 可以访问。
    • 请求头不携带token不可以访问。

2、给网站使用的api

  • 拦截 所有的请求,但是不处理/api/**开头的请求。
  • 所有的请求需要ROLE_ADMIN的权限。
  • 没有权限,需要使用表单登录。
  • 登录成功后,访问了无权限的请求,直接跳转到百度去。
  • 构建2个内建的用户
    • 用户一: admin/admin 拥有 ROLE_ADMIN 角色
    • 用户二:dev/dev 拥有 ROLE_DEV 角色
  • 访问 /index 端点
    • admin 用户访问,可以访问。
    • dev 用户访问,不可以访问,权限不够。

三、实现方案

方案一:

直接拆成多个服务,其中 /api/** 的成为一个服务。非/api/**的拆成另外一个服务。各个服务使用自己的配置,互不影响。

方案二

在同一个服务中编写。不同的请求使用不同的SecurityFilterChain来实现。

经过考虑,此处采用方案二来实现,因为方案一简单,使用方案二实现,也可以记录下在同一个项目中 通过使用多条过滤器链,因为并不是所有的时候,都是可以分成多个项目的。

扩展:

1、Spring Security SecurityFilterChain 的结构

2、控制 SecurityFilterChain 的执行顺序

使用 org.springframework.core.annotation.Order 注解。

3、查看是怎样选择那个 SecurityFilterChain

查看 org.springframework.web.filter.DelegatingFilterProxy#doFilter方法

四、实现

1、app 端 Spring Security 的配置

package com.huan.study.security.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;

/**
 * 给 app 端用的 Security 配置
 *
 * @author huan.fu 2021/7/13 - 下午9:06
 */
@Configuration
public class AppSecurityConfig {

    /**
     * 处理 给 app(前后端分离) 端使用的过滤链
     * 以 json 的数据格式返回给前端
     */
    @Bean
    @Order(1)
    public SecurityFilterChain appSecurityFilterChain(HttpSecurity http) throws Exception {
        // 只处理 /api 开头的请求
        return http.antMatcher("/api/**")
                .authorizeRequests()
                // 所有以 /api 开头的请求都需要 ADMIN 的权限
                    .antMatchers("/api/**")
                    .hasRole("ADMIN")
                    .and()
                // 捕获到异常,直接给前端返回 json 串
                .exceptionHandling()
                    .authenticationEntryPoint((request, response, authException) -> {
                        response.setStatus(HttpStatus.UNAUTHORIZED.value());
                        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
                        response.setContentType(MediaType.APPLICATION_JSON.toString());
                        response.getWriter().write("{\"message:\":\"您无权访问01\"}");
                    })
                    .accessDeniedHandler((request, response, accessDeniedException) -> {
                        response.setStatus(HttpStatus.UNAUTHORIZED.value());
                        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
                        response.setContentType(MediaType.APPLICATION_JSON.toString());
                        response.getWriter().write("{\"message:\":\"您无权访问02\"}");
                    })
                    .and()
                // 用户认证
                .addFilterBefore((request, response, chain) -> {
                    // 此处可以模拟从 token 中解析出用户名、权限等
                    String token = ((HttpServletRequest) request).getHeader("token");
                    if (!StringUtils.hasText(token)) {
                        chain.doFilter(request, response);
                        return;
                    }
                    Authentication authentication = new TestingAuthenticationToken(token, null,
                            AuthorityUtils.createAuthorityList("ROLE_ADMIN"));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    chain.doFilter(request, response);
                }, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}

2、网站端 Spring Secuirty 的配置

package com.huan.study.security.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

/**
 * 给 网站 应用的安全配置
 *
 * @author huan.fu 2021/7/14 - 上午9:09
 */
@Configuration
public class WebSiteSecurityFilterChainConfig {
    /**
     * 处理 给 webSite(非前后端分离) 端使用的过滤链
     * 以 页面 的格式返回给前端
     */
    @Bean
    @Order(2)
    public SecurityFilterChain webSiteSecurityFilterChain(HttpSecurity http) throws Exception {

        AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);

        // 创建用户
        authenticationManagerBuilder.inMemoryAuthentication()
                .withUser("admin")
                    .password(new BCryptPasswordEncoder().encode("admin"))
                    .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"))
                    .and()
                .withUser("dev")
                    .password(new BCryptPasswordEncoder().encode("dev"))
                    .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_DEV"))
                    .and()
                .passwordEncoder(new BCryptPasswordEncoder());

        // 只处理 所有 开头的请求
        return http.antMatcher("/**")
                .authorizeRequests()
                // 所有请求都必须要认证才可以访问
                    .anyRequest()
                    .hasRole("ADMIN")
                    .and()
                // 禁用csrf
                .csrf()
                    .disable()
                // 启用表单登录
                .formLogin()
                    .permitAll()
                    .and()
                // 捕获成功认证后无权限访问异常,直接跳转到 百度
                .exceptionHandling()
                    .accessDeniedHandler((request, response, exception) -> {
                        response.sendRedirect("http://www.baidu.com");
                    })
                    .and()
                .build();
    }

    /**
     * 忽略静态资源
     */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer( ){
        return web -> web.ignoring()
                .antMatchers("/**/js/**")
                .antMatchers("/**/css/**");

    }
}

3、控制器写法

/**
 * 资源控制器
 *
 * @author huan.fu 2021/7/13 - 下午9:33
 */
@Controller
public class ResourceController {

    /**
     * 返回用户信息
     */
    @GetMapping("/api/userInfo")
    @ResponseBody
    public Authentication showUserInfoApi() {
        return SecurityContextHolder.getContext().getAuthentication();
    }

    @GetMapping("/index")
    public String index(Model model){
        model.addAttribute("username","张三");
        return "index";
    }
}

4、引入jar包

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

五、实现效果

1、app 有权限访问 api

2、app 无权限访问 api

3、admin 用户有权限访问 网站 api

4、dev 用户无权限访问 网站 api

访问无权限的API直接跳转到 百度 首页。

六、完整代码

https://gitee.com/huan1993/Spring-Security/tree/master/multi-security-filter-chain

到此这篇关于Spring Security 多过滤链的使用详解的文章就介绍到这了,更多相关Spring Security 多过滤链 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring-全面详解(学习总结)

    目录 一.Spring介绍 简介 特点.主要特点 为什么要学? 二.IOC(依赖注入) Spring 容器IOC 和 bean介绍 控制反转: 容器概述: bean介绍 AOP 总结 一.Spring介绍 简介 Spring 框架为现代基于 java 的企业应用程序提供了一个全面的编程和配置模型--在任何类型的部署平台上 特点.主要特点 1.非侵入式 2.容器 3.IoC 4.AOP 5.方便程序的测试 为什么要学? 主流市场推荐.学习的人数多.Spring社区活跃.工作需要.属于开源框架.方便

  • springboot通过注解、接口创建定时任务详解

    目录 springboot中定时任务的创建 springboot通过注解创建定时任务 首先引入pom 直接上代码来一个栗子 @Scheduled注解的各个参数 springboot通过注接口创建定时任务 实现接口SchedulingConfigurer 主要方法 总结 项目中经常会用到定时任务,有的人在用quartz,有的人可能自己搭建了一套调度平台,springboot对于定任务的支持,让定时任务的创建变得简单,今天来说说springboot中定时任务的创建. springboot中定时任务的

  • 带你了解Spring AOP的使用详解

    目录 springmvc.xml BankDao AdminCheck BankDaoImpl LogInfo Transmaction AdminCheckInterceptor LogInfoInceptor TransmactionInterceptor Test 总结 springmvc.xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.

  • Springboot项目优雅地处理日志的方法详解

    如上图,每天会生成一个新的日志文件,然后日志进行分类,我这里只对error和info进行分类. 怎么做呢? 首先,在resource目录创建一个新文件,取名logback-spring.xml <?xml version="1.0" encoding="UTF-8" ?> <configuration > <appender name="consoleLog" class="ch.qos.logback.c

  • 详解关于spring bean名称命名的那些事

    目录 前言 02源码查看 01从main方法直接调试断点 02带着问题查看,靠猜加验证的方式 03源码验证 04总结 前言 用了多年spring,一直想当然把spring默认的beanName当成是类名的首字母小写,比如HelloService其beanName为helloService.直到有天对接了供方厂商的接口,他有个类形如ABService,于是用 getBean("aBService") 的方式获取bean,结果取到是null,一开始以为是ABservice没注入,后面采用

  • Spring Security 多过滤链的使用详解

    目录 一.背景 二.需求 1.给客户端使用的api 2.给网站使用的api 三.实现方案 方案一: 方案二 四.实现 1.app 端 Spring Security 的配置 五.实现效果 1.app 有权限访问 api 2.app 无权限访问 api 3.admin 用户有权限访问 网站 api 4.dev 用户无权限访问 网站 api 六.完整代码 一.背景 在我们实际的开发过程中,有些时候可能存在这么一些情况,某些api 比如: /api/** 这些是给App端使用的,数据的返回都是以JSO

  • Springboot集成Spring Security实现JWT认证的步骤详解

    1 简介 Spring Security作为成熟且强大的安全框架,得到许多大厂的青睐.而作为前后端分离的SSO方案,JWT也在许多项目中应用.本文将介绍如何通过Spring Security实现JWT认证. 用户与服务器交互大概如下: 客户端获取JWT,一般通过POST方法把用户名/密码传给server: 服务端接收到客户端的请求后,会检验用户名/密码是否正确,如果正确则生成JWT并返回:不正确则返回错误: 客户端拿到JWT后,在有效期内都可以通过JWT来访问资源了,一般把JWT放在请求头:一次

  • Spring Security动态权限的实现方法详解

    目录 1. 动态管理权限规则 1.1 数据库设计 1.2 实战 2. 测试 最近在做 TienChin 项目,用的是 RuoYi-Vue 脚手架,在这个脚手架中,访问某个接口需要什么权限,这个是在代码中硬编码的,具体怎么实现的,松哥下篇文章来和大家分析,有的小伙伴可能希望能让这个东西像 vhr 一样,可以在数据库中动态配置,因此这篇文章和小伙伴们简单介绍下 Spring Security 中的动态权限方案,以便于小伙伴们更好的理解 TienChin 项目中的权限方案. 1. 动态管理权限规则 通

  • Spring Security基于json登录实现过程详解

    主要是重写attemptAuthentication方法 导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot&l

  • Spring Security实现不同接口安全策略方法详解

    1. 前言 欢迎阅读 Spring Security 实战干货 系列文章 .最近有开发小伙伴提了一个有趣的问题.他正在做一个项目,涉及两种风格,一种是给小程序出接口,安全上使用无状态的JWT Token:另一种是管理后台使用的是Freemarker,也就是前后端不分离的Session机制.用Spring Security该怎么办? 2. 解决方案 我们可以通过多次继承WebSecurityConfigurerAdapter构建多个HttpSecurity.HttpSecurity 对象会告诉我们

  • Spring Security过滤器链体系的实例详解

    以下摘自胖哥分享的 2022开工福利教程. 在学习Spring Security的时候你有没有下面这两个疑问: Spring Security的登录是怎么配置的? Spring Security的访问控制是什么机制? SpringBootWebSecurityConfiguration 上面两个疑问的答案就在配置类SpringBootWebSecurityConfiguration中.你可以按照下面这个思维导图去理解这个自动配置: SpringBootWebSecurityConfigurati

  • 话说Spring Security权限管理(源码详解)

    最近项目需要用到Spring Security的权限控制,故花了点时间简单的去看了一下其权限控制相关的源码(版本为4.2). AccessDecisionManager spring security是通过AccessDecisionManager进行授权管理的,先来张官方图镇楼. AccessDecisionManager AccessDecisionManager 接口定义了如下方法: //调用AccessDecisionVoter进行投票(关键方法) void decide(Authent

  • Spring Security Remember me使用及原理详解

    Remember me功能就是勾选"记住我"后,一次登录,后面在有效期内免登录. 先看具体配置: pom文件: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <group

  • Spring Security自定义登录原理及实现详解

    1. 前言 前面的关于 Spring Security 相关的文章只是一个预热.为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始.安全访问的第一步就是认证(Authentication),认证的第一步就是登录.今天我们要通过对 Spring Security 的自定义,来设计一个可扩展,可伸缩的 form 登录功能. 2. form 登录的流程 下面是 form 登录的基本流程: 只要是 form 登录基本都能转化为上面的流程.接下来我们看看 Spring

  • Spring Security登录表单配置示例详解

    目录 Spring Security登录表单配置 1.引入pom依赖 2.bootstrap.yml添加配置 3.创建login.html 4.创建配置类 5.配置细节 6.登陆成功 7.登陆失败 8.注销登录 Spring Security登录表单配置 1.引入pom依赖 ​ 创建一个Spring Boot工程,引入Web和Spring Security依赖: <?xml version="1.0" encoding="UTF-8"?> <pro

随机推荐