Spring Security 多过滤链的使用详解

网友投稿 323 2022-12-27

Spring Security 多过滤链的使用详解

目录一、背景二、需求1、给客户端使用的api2、给网站使用的api三、实现方案方案一:方案二四、实现1、app 端 Spring Security 的配置五、实现效果1、app 有权限访问 api2、app 无权限访问 api3、admin 用户有权限访问 网站 api4、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;

impERnxBort org.springframework.http.MediaType;

import org.springframework.security.authentication.TestingAuthenticationToken;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.corehttp://.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")

.ahttp://nd()

// 禁用csrf

.csrf()

.disable()

// 启用表单登录

.formLogin()

.permitAll()

.and()

// 捕获成功认证后无权限访问异常,直接跳转到 百度

.exceptionHandling()

.accessDeniedHandler((request, response, exception) -> {

response.sendRedirect("http://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 chttp://lass 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包

org.springframework.boot

spring-boot-starter-security

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-thymeleaf

五、实现效果

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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:jedis获取redis中二进制图片转Base64方式
下一篇:阿里大数据 免费调用(阿里大数据应用平台)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~