Spring Security 多过滤链的使用

一、背景

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

二、需求

1、给客户端使用的api

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

2、给网站使用的api

1.拦截 所有的请求,但是不处理/api/**开头的请求。
2.所有的请求需要ROLE_ADMIN的权限。
3.没有权限,需要使用表单登录。
4.登录成功后,访问了无权限的请求,直接跳转到百度去。
5.构建2个内建的用户1.用户一: admin/admin 拥有 ROLE_ADMIN 角色2.用户二:dev/dev 拥有 ROLE_DEV 角色
6.访问 /index 端点1.admin 用户访问,可以访问。2.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();}/** * 忽略静态资源 */@Beanpublic 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")@ResponseBodypublic 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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值