Home >Java >Does spring-security filter get session information from redis?

Does spring-security filter get session information from redis?

PHPz
PHPzforward
2024-02-09 22:30:08494browse

php editor Zimo is here to answer the question about whether the Spring Security filter obtains session information from Redis. Spring Security is a powerful security framework that provides a complete set of authentication and authorization mechanisms. By default, Spring Security uses HttpSession to manage user session information. However, with configuration, we can store session information in external storage such as Redis. The advantage of this is that it can realize distributed session management and improve the scalability of the system. Therefore, Spring Security filters can obtain session information from Redis.

Question content

I am trying to use spring-boot, spring-security and spring-session to implement the login system, and use redis as the storage of the session.

My configuration:

@enablewebsecurity
@enablemethodsecurity
@configuration
@requiredargsconstructor
public class securityconfig {


    private final userdetailsservice detailsservice;

    @bean
    public passwordencoder passwordencoder() {
        return new bcryptpasswordencoder();
    }

    @bean
    public authenticationprovider authenticationprovider(passwordencoder passwordencoder) {
        daoauthenticationprovider provider = new daoauthenticationprovider();
        provider.setpasswordencoder(passwordencoder);
        provider.setuserdetailsservice(this.detailsservice);
        return provider;
    }

    @bean
    public authenticationmanager authenticationmanager(authenticationprovider authenticationprovider) {
        return new providermanager(authenticationprovider);
    }

    @bean
    public securityfilterchain filterchain(httpsecurity http) throws exception {
        return http
                .csrf().disable()
                .cors(customizer.withdefaults())
                .authorizehttprequests(auth -> {
                    auth.requestmatchers("/api/v1/auth/register/**", "/api/v1/auth/login").permitall();
                    auth.anyrequest().authenticated();
                })
                .sessionmanagement(sessionmanagement -> sessionmanagement
                        .sessioncreationpolicy(if_required) //
                        .sessionfixation(sessionmanagementconfigurer.sessionfixationconfigurer::newsession) //
                        .maximumsessions(100000) //
                        //.sessionregistry(sessionregistry())
                )
                //.exceptionhandling((ex) -> ex.authenticationentrypoint(this.authentrypoint))
                .logout(out -> out
                        .logouturl("/api/v1/auth/logout")
                        .invalidatehttpsession(true) // invalidate all sessions after logout
                        .deletecookies("jsessionid")
                        .logoutsuccesshandler((request, response, authentication) ->
                                securitycontextholder.clearcontext()
                        )
                )
                .build();
    }

    @bean
    public securitycontextrepository securitycontextrepository() {
        return new httpsessionsecuritycontextrepository();
    }
}

My login controller:

@postmapping("/login")
public void login(@requestbody loginform form, httpservletrequest request, httpservletresponse response) {
    string ip = httprequestutil.getip(request);
    string device = httprequestutil.getdevice(request);

    loginformwrapper loginformwrapper = new loginformwrapper(form.email(), form.password(), ip, device);

    authenticationservice.login(loginformwrapper, request, response);
}

and authentication service:

@override
public void login(loginformwrapper form, httpservletrequest request, httpservletresponse response) {
    authentication authentication = authenticationmanager.authenticate(usernamepasswordauthenticationtoken.unauthenticated(
            form.email().trim(), form.password()));

    // create a new context
    securitycontext context = securitycontextholder.createemptycontext();
    context.setauthentication(authentication);

    // update securitycontextholder and strategy
    this.securitycontextholderstrategy.setcontext(context);
    this.securitycontextrepository.savecontext(context, request, response);
}

If I understand correctly

this.securitycontextholderstrategy.setcontext(context); Authentication should be kept in the application's memory, e.g. in a threadlocal context

and

`this.securitycontextrepository.savecontext(context, request, response);`

Session information should be saved to redis.

Now when I log in I see the data has been saved to redis:

However, when inspecting what is returned by my login request, I see:

Completely different session id.

My first question is: Why don't these ids match? How does spring know which key to look for?

Another question is: what filter to get data from redis? I try to debug all filters in the filter chain:

[org.springframework.security.web.session.DisableEncodeUrlFilter@2fedae96,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@4945cd1f,
org.springframework.security.web.context.SecurityContextHolderFilter@72499396,
org.springframework.security.web.header.HeaderWriterFilter@7048d039,
org.springframework.web.filter.CorsFilter@2dbfcbe4,
org.springframework.security.web.authentication.logout.LogoutFilter@5d5a77de,
org.springframework.security.web.session.ConcurrentSessionFilter@1f8e1096,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@651bec9a,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@76d4e1af,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6f13ed1,
org.springframework.security.web.session.SessionManagementFilter@642693c2,
org.springframework.security.web.access.ExceptionTranslationFilter@2199e1a4,
org.springframework.security.web.access.intercept.AuthorizationFilter@48c584c]

But it seems to read the session information from the httpserletrequest somehow - however, if I remove the key from redis, the authentication fails for the endpoint that requires it.

Did I miss something? Do I retrieve the session information from redis and store it in httpservlerrequest before my fitler starts? Or how does it read redis data?

thanks for your help.

Solution

The value in the session cookie is base64 encoded:

echo '3c048eae-9f73-4df5-a009-bdf802ae37ca' | openssl base64
m2mwndhlywutowy3my00zgy1lwewmdktymrmodayywuzn2nhcg==
echo 'M2MwNDhlYWUtOWY3My00ZGY1LWEwMDktYmRmODAyYWUzN2NhCg==' | openssl base64 -d
3c048eae-9f73-4df5-a009-bdf802ae37ca

So when base64 decoded, the session id of the cookie matches the session id stored in redis.

If you haven't read it yet, I would recommend this document: https://www.php.cn/link/e27c71957d1e6c223e0d48a165da2ee1

Especially the "Understanding Components of Session Management" section: https://www.php.cn/link/e27c71957d1e6c223e0d48a165da2ee1#understanding-session-management-components

You didn't mention which version of spring security you are using, but I'm guessing you are using spring security 6. In this section, there is such a sentence related to sessionauthentication:

The above is the detailed content of Does spring-security filter get session information from redis?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete