ChatGPT解决这个技术问题 Extra ChatGPT

Spring Test & Security:如何模拟身份验证?

我试图弄清楚如何对我的控制器的 URL 是否正确保护进行单元测试。以防万一有人更改并意外删除了安全设置。

我的控制器方法如下所示:

@RequestMapping("/api/v1/resource/test") 
@Secured("ROLE_USER")
public @ResonseBody String test() {
    return "test";
}

我像这样设置了一个 WebTestEnvironment:

import javax.annotation.Resource;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({ 
        "file:src/main/webapp/WEB-INF/spring/security.xml",
        "file:src/main/webapp/WEB-INF/spring/applicationContext.xml",
        "file:src/main/webapp/WEB-INF/spring/servlet-context.xml" })
public class WebappTestEnvironment2 {

    @Resource
    private FilterChainProxy springSecurityFilterChain;

    @Autowired
    @Qualifier("databaseUserService")
    protected UserDetailsService userDetailsService;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    protected DataSource dataSource;

    protected MockMvc mockMvc;

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    protected UsernamePasswordAuthenticationToken getPrincipal(String username) {

        UserDetails user = this.userDetailsService.loadUserByUsername(username);

        UsernamePasswordAuthenticationToken authentication = 
                new UsernamePasswordAuthenticationToken(
                        user, 
                        user.getPassword(), 
                        user.getAuthorities());

        return authentication;
    }

    @Before
    public void setupMockMvc() throws NamingException {

        // setup mock MVC
        this.mockMvc = MockMvcBuilders
                .webAppContextSetup(this.wac)
                .addFilters(this.springSecurityFilterChain)
                .build();
    }
}

在我的实际测试中,我尝试做这样的事情:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import eu.ubicon.webapp.test.WebappTestEnvironment;

public class CopyOfClaimTest extends WebappTestEnvironment {

    @Test
    public void signedIn() throws Exception {

        UsernamePasswordAuthenticationToken principal = 
                this.getPrincipal("test1");

        SecurityContextHolder.getContext().setAuthentication(principal);        

        super.mockMvc
            .perform(
                    get("/api/v1/resource/test")
//                    .principal(principal)
                    .session(session))
            .andExpect(status().isOk());
    }

}

我在这里捡到了这个:

http://java.dzone.com/articles/spring-test-mvc-junit-testing 这里:

http://techdive.in/solutions/how-mock-securitycontextholder-perfrom-junit-tests-spring-controller 或这里:

如何JUnit测试@PreAuthorize注释及其由spring MVC控制器指定的spring EL?

然而,如果仔细观察,这仅在不向 URL 发送实际请求时才有帮助,而仅在功能级别测试服务时才有用。在我的情况下,引发了“拒绝访问”异常:

org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.1.RELEASE.jar:3.2.1.RELEASE]
        ...

以下两条日志消息值得注意,基本上说没有用户通过身份验证,表明设置 Principal 不起作用,或者它被覆盖。

14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List test.TestController.test(); target is of class [test.TestController]; Attributes: [ROLE_USER]
14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
您的公司名称 eu.ubicon 将显示在您的导入中。这不是安全隐患吗?
您好,感谢您的评论!我不明白为什么。无论如何,它是开源软件。如果您有兴趣,请参阅 bitbucket.org/ubicon/ubicon(或 bitbucket.org/dmir_wue/everyaware 了解最新的分叉)。如果我错过了什么,请告诉我。
检查此解决方案(答案是春季 4):stackoverflow.com/questions/14308341/…

C
Community

寻找答案我找不到任何既简单又灵活的方法,然后我找到了Spring Security Reference,我意识到有近乎完美的解决方案。 AOP 解决方案通常是最适合测试的解决方案,Spring 为它提供了 @WithMockUser@WithUserDetails@WithSecurityContext,在这个工件中:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>4.2.2.RELEASE</version>
    <scope>test</scope>
</dependency>

在大多数情况下,@WithUserDetails 收集了我需要的灵活性和功能。

@WithUserDetails 是如何工作的?

基本上,您只需要创建一个自定义 UserDetailsService,其中包含您想要测试的所有可能的用户配置文件。例如

@TestConfiguration
public class SpringSecurityWebAuxTestConfig {

    @Bean
    @Primary
    public UserDetailsService userDetailsService() {
        User basicUser = new UserImpl("Basic User", "user@company.com", "password");
        UserActive basicActiveUser = new UserActive(basicUser, Arrays.asList(
                new SimpleGrantedAuthority("ROLE_USER"),
                new SimpleGrantedAuthority("PERM_FOO_READ")
        ));

        User managerUser = new UserImpl("Manager User", "manager@company.com", "password");
        UserActive managerActiveUser = new UserActive(managerUser, Arrays.asList(
                new SimpleGrantedAuthority("ROLE_MANAGER"),
                new SimpleGrantedAuthority("PERM_FOO_READ"),
                new SimpleGrantedAuthority("PERM_FOO_WRITE"),
                new SimpleGrantedAuthority("PERM_FOO_MANAGE")
        ));

        return new InMemoryUserDetailsManager(Arrays.asList(
                basicActiveUser, managerActiveUser
        ));
    }
}

现在我们的用户已经准备好了,想象一下我们要测试这个控制器函数的访问控制:

@RestController
@RequestMapping("/foo")
public class FooController {

    @Secured("ROLE_MANAGER")
    @GetMapping("/salute")
    public String saluteYourManager(@AuthenticationPrincipal User activeUser)
    {
        return String.format("Hi %s. Foo salutes you!", activeUser.getUsername());
    }
}

这里我们有一个 get mapped function 到路由 /foo/salute 并且我们正在使用 @Secured 注释测试基于角色的安全性,尽管您可以测试 @PreAuthorize@PostAuthorize 也是如此。让我们创建两个测试,一个检查有效用户是否可以看到这个敬礼响应,另一个检查它是否真的被禁止。

@RunWith(SpringRunner.class)
@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = SpringSecurityWebAuxTestConfig.class
)
@AutoConfigureMockMvc
public class WebApplicationSecurityTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithUserDetails("manager@company.com")
    public void givenManagerUser_whenGetFooSalute_thenOk() throws Exception
    {
        mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
                .accept(MediaType.ALL))
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("manager@company.com")));
    }

    @Test
    @WithUserDetails("user@company.com")
    public void givenBasicUser_whenGetFooSalute_thenForbidden() throws Exception
    {
        mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
                .accept(MediaType.ALL))
                .andExpect(status().isForbidden());
    }
}

如您所见,我们导入了 SpringSecurityWebAuxTestConfig 来为我们的用户提供测试。每一个都在其对应的测试用例上使用,只需使用简单的注释,减少代码和复杂性。

更好地使用 @WithMockUser 以获得更简单的基于角色的安全性

如您所见,@WithUserDetails 具有大多数应用程序所需的所有灵活性。它允许您使用具有任何 GrantedAuthority 的自定义用户,例如角色或权限。但是,如果您只是使用角色,则测试会更加容易,并且您可以避免构建自定义 UserDetailsService。在这种情况下,使用 @WithMockUser 指定用户、密码和角色的简单组合。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@WithSecurityContext(
    factory = WithMockUserSecurityContextFactory.class
)
public @interface WithMockUser {
    String value() default "user";

    String username() default "";

    String[] roles() default {"USER"};

    String password() default "password";
}

注释为非常基本的用户定义了默认值。在我们的例子中,我们正在测试的路由只要求经过身份验证的用户是管理员,我们可以使用 SpringSecurityWebAuxTestConfig 退出并执行此操作。

@Test
@WithMockUser(roles = "MANAGER")
public void givenManagerUser_whenGetFooSalute_thenOk() throws Exception
{
    mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
            .accept(MediaType.ALL))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("user")));
}

请注意,现在我们得到的是 @WithMockUser 提供的默认值,而不是用户 manager@company.comuser;但这并不重要,因为我们真正关心的是他的角色:ROLE_MANAGER

结论

正如您看到的 @WithUserDetails@WithMockUser 等注释,我们可以在不同的经过身份验证的用户场景之间切换,而无需构建与我们的架构疏远的类,而只是为了进行简单的测试。它还建议您了解 @WithSecurityContext 如何工作以获得更大的灵活性。


如何模拟多个用户?例如,第一个请求由 tom 发送,而第二个请求由 jerry 发送?
您可以使用 tom 创建一个函数,并使用相同的逻辑创建另一个测试并使用 Jerry 进行测试。每个测试都会有一个特定的结果,因此会有不同的断言,如果测试失败,它会通过它的名称告诉你哪个用户/角色没有工作。请记住,在一个请求中,用户只能是一个,因此在一个请求中指定多个用户是没有意义的。
抱歉,我的意思是这样的示例场景:我们对此进行了测试,tom 创建了一篇秘密文章,然后 jerry 尝试阅读该文章,而 jerry 不应该看到它(因为它是秘密的)。所以在这种情况下,它是一个单元测试......
它看起来很像答案中给出的 BasicUserManager User 场景。关键概念是,我们实际上关心他们的角色,而不是关心用户,但是这些测试中的每一个,位于同一个单元测试中,实际上代表不同的查询。由不同用户(具有不同角色)对同一端点完成。
G
GummyBear21

从 Spring 4.0+ 开始,最好的解决方案是使用 @WithMockUser 注释测试方法

@Test
@WithMockUser(username = "user1", password = "pwd", roles = "USER")
public void mytest1() throws Exception {
    mockMvc.perform(get("/someApi"))
        .andExpect(status().isOk());
}

请记住将以下依赖项添加到您的项目中

'org.springframework.security:spring-security-test:4.2.3.RELEASE'

春天真奇妙。谢谢
好答案。此外-您不需要使用mockMvc,但是如果您使用springframework.data中的例如PagingAndSortingRepository-您可以直接从存储库中调用方法(使用EL @PreAuthorize(......)进行注释)
谢谢,@WithMockUser(roles = "YOUR_ROLE") 会变得更好
如何添加 orgId 以及
只是一个小小的提醒,您不必将版本显式添加到您的 spring 依赖项
L
Liam

事实证明,作为 Spring Security 过滤器链的一部分的 SecurityContextPersistenceFilter 总是重置我的 SecurityContext,我设置它调用 SecurityContextHolder.getContext().setAuthentication(principal)(或使用 .principal(principal) 方法)。此过滤器将 SecurityContextHolder 中的 SecurityContext 设置为 SecurityContextRepository OVERWRITING 中的 SecurityContext,这是我之前设置的。默认情况下,存储库是 HttpSessionSecurityContextRepositoryHttpSessionSecurityContextRepository 检查给定的 HttpRequest 并尝试访问相应的 HttpSession。如果存在,它将尝试从 HttpSession 读取 SecurityContext。如果失败,存储库会生成一个空的 SecurityContext

因此,我的解决方案是将 HttpSession 与包含 SecurityContext 的请求一起传递:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import eu.ubicon.webapp.test.WebappTestEnvironment;

public class Test extends WebappTestEnvironment {

    public static class MockSecurityContext implements SecurityContext {

        private static final long serialVersionUID = -1386535243513362694L;

        private Authentication authentication;

        public MockSecurityContext(Authentication authentication) {
            this.authentication = authentication;
        }

        @Override
        public Authentication getAuthentication() {
            return this.authentication;
        }

        @Override
        public void setAuthentication(Authentication authentication) {
            this.authentication = authentication;
        }
    }

    @Test
    public void signedIn() throws Exception {

        UsernamePasswordAuthenticationToken principal = 
                this.getPrincipal("test1");

        MockHttpSession session = new MockHttpSession();
        session.setAttribute(
                HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, 
                new MockSecurityContext(principal));


        super.mockMvc
            .perform(
                    get("/api/v1/resource/test")
                    .session(session))
            .andExpect(status().isOk());
    }
}

我们尚未添加对 Spring Security 的官方支持。请参阅 jira.springsource.org/browse/SEC-2015 github.com/SpringSource/spring-test-mvc/blob/master/src/test/… 中指定了其外观的轮廓
我不认为创建一个 Authentication 对象并添加一个具有相应属性的会话是那么糟糕。你认为这是一个有效的“变通办法”吗?另一方面,直接支持当然很棒。看起来很整洁。感谢您的链接!
很好的解决方案。为我工作!受保护方法 getPrincipal() 的命名只是一个小问题,我认为这有点误导。理想情况下,它应该被命名为 getAuthentication()。同样,在您的 signedIn() 测试中,局部变量应命名为 authauthentication 而不是 principal
什么是 "getPrincipal("test1") ¿?? 你能解释一下它在哪里吗?提前谢谢
@user2992476 它可能返回一个 UsernamePasswordAuthenticationToken 类型的对象。或者,您创建 GrantedAuthority 并构造此对象。
p
px06

在 pom.xml 中添加:

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <version>4.0.0.RC2</version>
    </dependency>

并使用 org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors 进行授权请求。请参阅 https://github.com/rwinch/spring-security-test-blog (https://jira.spring.io/browse/SEC-2592) 中的示例用法。

更新:

4.0.0.RC2 适用于 spring-security 3.x。对于 spring-security 4 spring-security-test 成为 spring-security 的一部分(http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test,版本相同)。

设置已更改:http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test-mockmvc

public void setup() {
    mvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())  
            .build();
}

基本身份验证示例:http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#testing-http-basic-authentication


这也解决了我在尝试通过登录安全过滤器登录时收到 404 的问题。谢谢!
嗨,在 GKislin 提到的测试时。我收到以下错误“身份验证失败 UserDetailsService 返回 null,这是违反接口合同”。请有任何建议。最终 AuthenticationRequest auth = new AuthenticationRequest(); auth.setUsername(userId); auth.setPassword(密码); mockMvc.perform(post("/api/auth/").content(json(auth)).contentType(MediaType.APPLICATION_JSON));
J
Jay

这是一个示例,供那些想要使用 Base64 基本身份验证测试 Spring MockMvc 安全配置的人使用。

String basicDigestHeaderValue = "Basic " + new String(Base64.encodeBase64(("<username>:<password>").getBytes()));
this.mockMvc.perform(get("</get/url>").header("Authorization", basicDigestHeaderValue).accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk());

Maven 依赖

    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.3</version>
    </dependency>

N
Nagy Attila

简短的回答:

@Autowired
private WebApplicationContext webApplicationContext;

@Autowired
private Filter springSecurityFilterChain;

@Before
public void setUp() throws Exception {
    final MockHttpServletRequestBuilder defaultRequestBuilder = get("/dummy-path");
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
            .defaultRequest(defaultRequestBuilder)
            .alwaysDo(result -> setSessionBackOnRequestBuilder(defaultRequestBuilder, result.getRequest()))
            .apply(springSecurity(springSecurityFilterChain))
            .build();
}

private MockHttpServletRequest setSessionBackOnRequestBuilder(final MockHttpServletRequestBuilder requestBuilder,
                                                             final MockHttpServletRequest request) {
    requestBuilder.session((MockHttpSession) request.getSession());
    return request;
}

从 spring security test 执行 formLogin 后,您的每个请求都将被自动调用为登录用户。

长答案:

检查此解决方案(答案是春季 4):How to login a user with spring 3.2 new mvc testing


P
Pavla Nováková

避免在测试中使用 SecurityContextHolder 的选项:

选项 1:使用模拟 - 我的意思是使用一些模拟库模拟 SecurityContextHolder - 例如 EasyMock

选项 2:在您的代码中的某些服务中包装调用 SecurityContextHolder.get... - 例如在 SecurityServiceImpl 中使用实现 SecurityService 接口的 getCurrentPrincipal 方法,然后在您的测试中,您可以简单地创建此接口的模拟实现,该接口无需访问即可返回所需的主体到 SecurityContextHolder。


嗯,也许我没有得到全貌。我的问题是 SecurityContextPersistenceFilter 使用来自 HttpSessionSecurityContextRepository 的 SecurityContext 替换了 SecurityContext,而后者又从相应的 HttpSession 中读取 SecurityContext。因此使用会话的解决方案。关于对 SecurityContextHolder 的调用:我编辑了我的答案,以便不再使用对 SecurityContextHolder 的调用。但也没有引入任何包装或额外的模拟库。您认为这是一个更好的解决方案吗?
抱歉,我不完全了解您在寻找什么,我无法提供比您提出的解决方案更好的答案 - 这似乎是一个不错的选择。
好的,谢谢。我现在将接受我的建议作为解决方案。
D
Deekshith Anand

虽然很晚的答案。但这对我有用,并且可能有用。

在使用 Spring Security 和 mockMvc 时,您只需使用 @WithMockUser 注释,就像提到的其他注释一样。

Spring security 还提供了另一个名为 @WithAnonymousUser 的注解,用于测试未经身份验证的请求。但是,您应该在这里小心。你会期待 401,但默认情况下我得到 403 Forbidden Error。在实际场景中,当您运行实际服务时,它会被重定向,最终您会得到正确的 401 响应代码。匿名请求使用此注解。

您也可以考虑省略注释并简单地保持未经授权。但这通常会引发正确的异常(如 AuthenticationException),但如果处理正确(如果您使用自定义处理程序),您将获得正确的状态代码。我曾经为此得到500。所以寻找调试器中引发的异常,检查是否处理正确并返回正确的状态码。


S
Sarvar N

在您的测试包上创建一个类 TestUserDetailsImpl

@Service
@Primary
@Profile("test")
public class TestUserDetailsImpl implements UserDetailsService {
    public static final String API_USER = "apiuser@example.com";

    private User getAdminUser() {
        User user = new User();
        user.setUsername(API_USER);

        SimpleGrantedAuthority role = new SimpleGrantedAuthority("ROLE_API_USER");
        user.setAuthorities(Collections.singletonList(role));

        return user;
    }

    @Override
    public UserDetails loadUserByUsername(String username) 
                                         throws UsernameNotFoundException {
        if (Objects.equals(username, ADMIN_USERNAME))
            return getAdminUser();
        throw new UsernameNotFoundException(username);
    }
}

休息端点:

@GetMapping("/invoice")
@Secured("ROLE_API_USER")
public Page<InvoiceDTO> getInvoices(){
   ...
}

测试端点:

@Test
@WithUserDetails("apiuser@example.com")
public void testApi() throws Exception {
     ...
}

A
Adrian

当使用 MockMvcBuilders.webAppContextSetup(wac).addFilters(...) 而不是 springSecurityFilterChain(更具体地说是 SecurityContextPersistenceFilter)将接管并删除 @WithMockUser 准备的 SecurityContext(很傻);发生这种情况是因为 SecurityContextPersistenceFilter 试图从 HttpSession 中“恢复”SecurityContext,但没有找到。好吧,使用下面定义的这个简单的 AutoStoreSecurityContextHttpFilter,它将负责将 @WithMockUser 准备好的 SecurityContext 放入 HttpSession,以便以后 SecurityContextPersistenceFilter 能够找到它。

@ContextConfiguration(...) // the issue doesn't occur when using @SpringBootTest
public class SomeTest {
    @Autowired
    private Filter springSecurityFilterChain;
    private MockMvc mockMvc;

    @BeforeEach
    void setup(WebApplicationContext wac) {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
                .addFilters(new AutoStoreSecurityContextHttpFilter(), springSecurityFilterChain).build();
    }

    @WithMockUser
    @Test
    void allowAccessToAuthenticated() {
        ...
    }
}

// don't use this Filter in production because it's only intended for tests, to solve the 
// @WithMockUser & springSecurityFilterChain (more specifically SecurityContextPersistenceFilter) "misunderstandings"
public class AutoStoreSecurityContextHttpFilter extends HttpFilter {
    protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
        req.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
        super.doFilter(req, res, chain);
    }
}