关于java: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();
    }

    /**
     * 疏忽动态资源
     */
    @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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理