SoFunction
Updated on 2025-04-14

Four solutions for SpringBoot to implement single sign-on (SSO)

introduction

Single Sign-On (SSO) is a common user authentication scheme in enterprise application systems. It allows users to access multiple related but independent systems using a set of credentials without repeated logins. For enterprises with multiple applications, SSO can significantly improve user experience and reduce credential management costs.

1. Traditional SSO solution based on Cookie-Session

principle

This is the most basic SSO implementation method. Its core is to store the user authentication status in the server session and save the Session identifier on the client through cookies. When the user logs into the SSO server, the server creates a session to store user information and sets the SessionID under the top-level domain name through a cookie, so that all subdomain applications can access the cookie and verify the same session.

Implementation plan

  • Create an SSO server
@RestController
@RequestMapping("/sso")
public class SsoServerController {

    @Autowired
    private UserService userService;
    
    @PostMapping("/login")
    public ResponseEntity<String> login(
            @RequestParam String username,
            @RequestParam String password,
            HttpServletRequest request,
            HttpServletResponse response,
            @RequestParam(required = false) String redirect) {
        
        // Verify user credentials        User user = (username, password);
        if (user == null) {
            return ().body("Invalid credentials");
        }
        
        // Create a session and store user information        HttpSession session = (true);
        ("USER_INFO", user);
        (3600); // Session valid for 1 hour        
        // Get SessionID        String sessionId = ();
        
        // Set cookies        Cookie cookie = new Cookie("SSO_SESSION_ID", sessionId);
        (3600); // 1 hour valid        ("/");
        ("."); // Key: Top-level domains, making all subdomains accessible        (true);
        (true); // Transfer via HTTPS only        
        (cookie);
        
        // If there is a redirect URL, redirect back to the original application        if (redirect != null && !()) {
            return ()
                .header("Location", redirect)
                .build();
        }
        
        return ("Login successful");
    }
    
    @GetMapping("/validate")
    public ResponseEntity<UserInfo> validateSession(
            @CookieValue(name = "SSO_SESSION_ID", required = false) String sessionId) {
        
        if (sessionId == null) {
            return ().build();
        }
        
        // Verify the validity of the Session and obtain user information        HttpSession session = (sessionId);
        if (session == null) {
            return ().build();
        }
        
        // Get user information from Session        User user = (User) ("USER_INFO");
        if (user == null) {
            return ().build();
        }
        
        // Return user information        UserInfo userInfo = new UserInfo((), (), ());
        return (userInfo);
    }
    
    @PostMapping("/logout")
    public ResponseEntity<Void> logout(
            @CookieValue(name = "SSO_SESSION_ID", required = false) String sessionId,
            HttpServletResponse response,
            @RequestParam(required = false) String redirect) {
        
        // Invalidate Session        if (sessionId != null) {
            HttpSession session = (sessionId);
            if (session != null) {
                ();
            }
        }
        
        // Delete Cookies        Cookie cookie = new Cookie("SSO_SESSION_ID", null);
        (0);
        ("/");
        (".");
        (cookie);
        
        // If there is a redirect URL, redirect        if (redirect != null && !()) {
            return ()
                .header("Location", redirect)
                .build();
        }
        
        return ().build();
    }
}
  • Client application integration
@Component
public class SsoFilter implements Filter {

    @Autowired
    private RestTemplate restTemplate;
    
    private static final String SSO_SERVER_URL = "";
    private static final String SSO_VALIDATION_URL = SSO_SERVER_URL + "/sso/validate";
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // Check whether the current application already has a local session        HttpSession currentSession = (false);
        if (currentSession != null && ("USER_INFO") != null) {
            // There is already a local session, continue to request            (request, response);
            return;
        }
        
        // Get SSO Session Cookies        Cookie[] cookies = ();
        String ssoSessionId = null;
        
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("SSO_SESSION_ID".equals(())) {
                    ssoSessionId = ();
                    break;
                }
            }
        }
        
        // SSO cookie is not found, redirect to SSO login page        if (ssoSessionId == null) {
            String redirectUrl = SSO_SERVER_URL + "/login?redirect=" + 
                                (().toString(), "UTF-8");
            (redirectUrl);
            return;
        }
        
        // Verify the validity of SSO Session        try {
            HttpHeaders headers = new HttpHeaders();
            ("Cookie", "SSO_SESSION_ID=" + ssoSessionId);
            HttpEntity<String> entity = new HttpEntity<>(headers);
            
            ResponseEntity<UserInfo> responseEntity = (
                    SSO_VALIDATION_URL, , entity, );
            
            if (() == ) {
                // SSO Session is valid, create a local Session                UserInfo userInfo = ();
                HttpSession session = (true);
                ("USER_INFO", userInfo);
                
                (request, response);
            } else {
                // SSO Session is invalid, redirect to login page                String redirectUrl = SSO_SERVER_URL + "/login?redirect=" + 
                                    (().toString(), "UTF-8");
                (redirectUrl);
            }
        } catch (Exception e) {
            // There was an error in the verification process, redirect to the login page            String redirectUrl = SSO_SERVER_URL + "/login?redirect=" + 
                                (().toString(), "UTF-8");
            (redirectUrl);
        }
    }
}

Pros and cons

advantage:

  • The implementation is relatively simple and follows the traditional web development model
  • The server fully controls the session state and life cycle
  • Clients do not need to store and manage complex states
  • Support instant session failure and revocation

shortcoming:

  • Restricted by the same origin policy, only applicable to applications under the same top-level domain name
  • Relying on cookie mechanisms may be restricted in certain environments (such as mobile applications)
  • There is a risk of CSRF

2. Stateless SSO scheme based on JWT

principle

JWT (JSON Web Token) is a compact, self-contained token format that can safely pass information between different applications. When implementing SSO using JWT, the authentication server generates a JWT token after the user logs in, which contains user-related information and signatures. Since JWT can be independently verified without querying a central server, it is ideal for building stateless SSO systems.

Implementation plan

  • Create a JWT certification service

First add dependencies:

<dependency>
    <groupId></groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId></groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId></groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

Implement JWT tool class:

@Component
public class JwtUtil {

    @Value("${}")
    private String secretKey;
    
    @Value("${}")
    private long expirationTime;
    
    @PostConstruct
    protected void init() {
        secretKey = ().encodeToString(());
    }
    
    public String generateToken(User user) {
        Map<String, Object> claims = new HashMap<>();
        ("id", ());
        ("username", ());
        ("email", ());
        ("roles", ());
        
        return createToken(claims, ());
    }
    
    private String createToken(Map<String, Object> claims, String subject) {
        Date now = new Date();
        Date expiryDate = new Date(() + expirationTime);
        
        return ()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith((()), SignatureAlgorithm.HS512)
                .compact();
    }
    
    public boolean validateToken(String token) {
        try {
            ()
                .setSigningKey((()))
                .build()
                .parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    
    public Claims extractAllClaims(String token) {
        return ()
                .setSigningKey((()))
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
}

Implement authentication controller:

@RestController
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private UserService userService;
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @PostMapping("/login")
    public ResponseEntity&lt;?&gt; login(@RequestBody LoginRequest loginRequest) {
        // Verify user credentials        User user = (
                (), 
                ()
        );
        
        if (user == null) {
            return ()
                    .body(new ErrorResponse("Invalid credentials"));
        }
        
        // Generate JWT        String jwt = (user);
        
        // Set refresh token (optional)        String refreshToken = ().toString();
        ((), refreshToken, 30); // 30-day validity period        
        // Return to Token        return (new JwtResponse(jwt, refreshToken));
    }
    
    @PostMapping("/refresh")
    public ResponseEntity&lt;?&gt; refreshToken(@RequestBody RefreshTokenRequest request) {
        String refreshToken = ();
        
        // Verify refresh token        User user = (refreshToken);
        if (user == null) {
            return ()
                    .body(new ErrorResponse("Invalid refresh token"));
        }
        
        // Generate a new JWT        String newToken = (user);
        
        return (new JwtResponse(newToken, refreshToken));
    }
    
    @PostMapping("/validate")
    public ResponseEntity&lt;?&gt; validateToken(@RequestBody TokenValidationRequest request) {
        String token = ();
        
        if (!(token)) {
            return ()
                    .body(new ErrorResponse("Invalid token"));
        }
        
        // Extract user information        Claims claims = (token);
        Map&lt;String, Object&gt; userInfo = new HashMap&lt;&gt;(claims);
        
        return (userInfo);
    }
    
    @PostMapping("/logout")
    public ResponseEntity&lt;?&gt; logout(@RequestBody LogoutRequest request) {
        // Delete the refresh token        (());
        
        // JWT itself cannot be revoked, the client needs to discard the token        return (new SuccessResponse("Logout successful"));
    }
}
  • Client application integration

JWT filter:

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        
        final String authorizationHeader = ("Authorization");
        
        String username = null;
        String jwt = null;
        
        // Extract JWT from Authorization header        if (authorizationHeader != null &amp;&amp; ("Bearer ")) {
            jwt = (7);
            try {
                // Verify the token and extract user information                if ((jwt)) {
                    Claims claims = (jwt);
                    username = ();
                    
                    // Build authentication objects                    List&lt;String&gt; roles = (List&lt;String&gt;) ("roles");
                    List&lt;GrantedAuthority&gt; authorities = ()
                            .map(SimpleGrantedAuthority::new)
                            .collect(());
                    
                    UserDetails userDetails = new User(username, "", authorities);
                    
                    UsernamePasswordAuthenticationToken authentication = 
                            new UsernamePasswordAuthenticationToken(
                                    userDetails, null, ());
                                    
                    (new WebAuthenticationDetailsSource().buildDetails(request));
                    ().setAuthentication(authentication);
                }
            } catch (Exception e) {
                // JWT verification failed, authentication information is not set            }
        }
        
        (request, response);
    }
}

Security configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ().disable()
            .authorizeRequests()
            .antMatchers("/api/public/**", "/auth/login", "/auth/refresh").permitAll()
            .anyRequest().authenticated()
            .and()
            .sessionManagement()
            .sessionCreationPolicy();
            
        (jwtRequestFilter, );
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return ();
    }
}
  • Front-end processing JWT
//Storage the JWT token after login is successfulfunction handleLoginSuccess(response) {
    const { token, refreshToken } = ;
    ('jwtToken', token);
    ('refreshToken', refreshToken);
    
    // Set the default Authorization header    ['Authorization'] = `Bearer ${token}`;
    
    // Redirect to the application page     = '/dashboard';
}

// Add request interceptor to automatically attach token(config =&gt; {
    const token = ('jwtToken');
    if (token) {
         = `Bearer ${token}`;
    }
    return config;
});

// Add a response interceptor to handle the expiration of tokens(
    response =&gt; response,
    async error =&gt; {
        const originalRequest = ;
        
        // If it is a 401 error and is not a refresh token request, try refreshing the token        if ( === 401 &amp;&amp; !originalRequest._retry) {
            originalRequest._retry = true;
            
            try {
                const refreshToken = ('refreshToken');
                const response = await ('/auth/refresh', { refreshToken });
                
                const { token } = ;
                ('jwtToken', token);
                ['Authorization'] = `Bearer ${token}`;
                
                return axios(originalRequest);
            } catch (err) {
                // Refresh failed, redirect to the login page                ('jwtToken');
                ('refreshToken');
                 = '/login';
                return (err);
            }
        }
        
        return (error);
    }
);

Pros and cons

advantage:

  • Completely stateless, the server does not need to store session information
  • Cross-domain support for distributed systems and microservices
  • Good scalability, JWT can contain rich user information
  • Don't rely on cookies to avoid CSRF problems
  • Suitable for various clients (Web, mobile applications, APIs)

shortcoming:

  • Unable to proactively invalidate issued tokens (unless blacklisting mechanism is used)
  • JWT may be larger, increasing the network transmission burden
  • Token management requires client intervention
  • Refresh token mechanism is more complex
  • There is a risk of tokens being stolen

3. SSO solution based on OAuth 2.0/OpenID Connect

principle

OAuth 2.0 is an authorization framework, while OpenID Connect (OIDC) is an authentication layer built on OAuth 2.0. This is the most standardized and perfect SSO solution at present, especially suitable for enterprise-level applications and scenarios where third-party integration is required. It provides a wealth of authorization process options and security features.

Implementation plan

In SpringBoot, Spring Security OAuth2 can be used to implement OAuth 2.0/OIDC servers and clients.

  • Build an authorized server

First add dependencies:

<dependency>
    <groupId></groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.6.8</version>
</dependency>
<dependency>
    <groupId></groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

Configure the authorization server:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private DataSource dataSource;
    
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        ("your-signing-key");
        return converter;
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }
    
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomTokenEnhancer();
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        (
                (tokenEnhancer(), accessTokenConverter()));
                
        endpoints
            .tokenStore(tokenStore())
            .tokenEnhancer(tokenEnhancerChain)
            .authenticationManager(authenticationManager)
            .userDetailsService(userDetailsService)
            .reuseRefreshTokens(false);
    }
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        (dataSource)
            .withClient("web-client")
            .secret(("web-client-secret"))
            .authorizedGrantTypes("password", "refresh_token", "authorization_code")
            .scopes("read", "write")
            .redirectUris("/login/oauth2/code/custom",
                          "/login/oauth2/code/custom")
            .accessTokenValiditySeconds(3600)
            .refreshTokenValiditySeconds(86400)
            .and()
            .withClient("mobile-client")
            .secret(("mobile-client-secret"))
            .authorizedGrantTypes("password", "refresh_token")
            .scopes("read")
            .accessTokenValiditySeconds(7200)
            .refreshTokenValiditySeconds(259200);
    }
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()")
            .allowFormAuthenticationForClients();
    }
}

// Custom token enhancer to add additional user informationpublic class CustomTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        if (() instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) ();
            Map&lt;String, Object&gt; additionalInfo = new HashMap&lt;&gt;();
            
            // Add additional user information            if (userDetails instanceof CustomUserDetails) {
                CustomUserDetails customUserDetails = (CustomUserDetails) userDetails;
                ("userId", ());
                ("email", ());
                ("fullName", ());
            }
            
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        }
        
        return accessToken;
    }
}

Configure the resource server:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        ()
            .antMatchers("/api/public/**").permitAll()
            .antMatchers("/api/user/**").hasRole("USER")
            .antMatchers("/api/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated();
    }
}
  • Client application integration

Client configuration():

spring:
  security:
    oauth2:
      client:
        registration:
          custom:
            client-id: web-client
            client-secret: web-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            scope: read,write
        provider:
          custom:
            authorization-uri: /oauth/authorize
            token-uri: /oauth/token
            user-info-uri: /userinfo
            jwk-set-uri: /.well-known/
            user-name-attribute: sub

Client security configuration:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests(authorizeRequests -&gt;
                authorizeRequests
                    .antMatchers("/", "/login/**", "/error", "/webjars/**").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2Login(oauth2Login -&gt;
                oauth2Login
                    .loginPage("/login")
                    .defaultSuccessUrl("/home")
                    .userInfoEndpoint()
                    .userService(oAuth2UserService())
            )
            .logout(logout -&gt;
                logout
                    .logoutSuccessUrl("/logout?client_id=web-client")
                    .invalidateHttpSession(true)
                    .clearAuthentication(true)
                    .deleteCookies("JSESSIONID")
            );
    }
    
    @Bean
    public OAuth2UserService&lt;OAuth2UserRequest, OAuth2User&gt; oAuth2UserService() {
        DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
        
        return userRequest -&gt; {
            OAuth2User oAuth2User = (userRequest);
            
            // Customize user information processing            Map&lt;String, Object&gt; attributes = ();
            String userNameAttributeName = ()
                    .getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
            
            return new DefaultOAuth2User(
                    (),
                    attributes,
                    userNameAttributeName);
        };
    }
}
  • Complete logout process
@Controller
public class AuthController {

    @GetMapping("/logout")
    public String logout(HttpServletRequest request,
                         HttpServletResponse response,
                         @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {
        
        // Clear local session        ();
        HttpSession session = (false);
        if (session != null) {
            ();
        }
        
        // Clear cookies        Cookie[] cookies = ();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                ("");
                ("/");
                (0);
                (cookie);
            }
        }
        
        // Redirect to the logout endpoint of the OAuth2 server        String logoutUrl = "/logout?client_&amp;post_logout_redirect_uri=" +
                ("/", StandardCharsets.UTF_8);
        
        return "redirect:" + logoutUrl;
    }
}

Pros and cons

advantage:

  • Mature security protocols, widely adopted industry standards
  • Supports multiple authentication processes (authorization code, implicit, password, etc.)
  • Token revocation mechanism is improved
  • Excellent scalability, suitable for enterprise-level applications
  • Clarify the separation of certification and authorization responsibilities

shortcoming:

  • High complexity in implementation, small applications may not be suitable
  • Steep learning curves for configuration and understanding

4. Shared session SSO solution based on Spring Session

principle

Spring Session provides a framework for storing session data in shared external storage (such as Redis), allowing session information to be shared between different applications. This approach is particularly suitable for Spring-based isomorphic systems, which can solve distributed session sharing problems while keeping simple implementations.

Implementation plan

  • Add dependencies
<dependency>
    <groupId></groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId></groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
  • Configuring Spring Session
@Configuration
@EnableRedisHttpSession
public class SessionConfig {

    @Bean
    public LettuceConnectionFactory connectionFactory() {
        // Redis connection configuration        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
        ("redis-host");
        (6379);
        
        return new LettuceConnectionFactory(redisConfig);
    }
    
    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        ("SESSION"); // Use a unified cookie name        ("/");
        ("^.+?\.(\w+\.[a-z]+)$"); // Support subdomains        (true);
        (true);
        
        return serializer;
    }
    
    @Bean
    public HttpSessionIdResolver httpSessionIdResolver() {
        return new CookieHttpSessionIdResolver();
    }
}
  • Create a central certification service
@Controller
@RequestMapping("/auth")
public class CentralAuthController {

    @Autowired
    private UserService userService;
    
    @GetMapping("/login")
    public String loginPage(@RequestParam(required = false) String redirect, Model model) {
        ("redirectUrl", redirect);
        return "login";
    }
    
    @PostMapping("/login")
    public String login(@RequestParam String username,
                       @RequestParam String password,
                       @RequestParam(required = false) String redirect,
                       HttpSession session,
                       RedirectAttributes redirectAttrs) {
        
        // Verify user credentials        User user = (username, password);
        if (user == null) {
            ("error", "Invalid credentials");
            return "redirect:/auth/login";
        }
        
        // Save user information into a shared session        UserInfo userInfo = new UserInfo((), (), ());
        ("USER_INFO", userInfo);
        
        // If there is a redirect URL, redirect back to the original application        if (redirect != null &amp;&amp; !()) {
            return "redirect:" + redirect;
        }
        
        return "redirect:/dashboard";
    }
    
    @GetMapping("/logout")
    public String logout(HttpServletRequest request, HttpSession session) {
        // Invalidate the session        ();
        
        // Optional: Get the URL to be redirected        String referer = ("Referer");
        
        return "redirect:/auth/login";
    }
}
  • Create in-app authentication filters
@Component
public class SessionAuthenticationFilter implements Filter {

    private static final String LOGIN_PAGE = "/auth/login";
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // No authentication is required for public path        String path = ();
        if (isPublicPath(path)) {
            (request, response);
            return;
        }
        
        // Check whether there is user information in the session        HttpSession session = (false);
        boolean authenticated = session != null &amp;&amp; ("USER_INFO") != null;
        
        if (authenticated) {
            // The user has been authenticated and continue to request            (request, response);
        } else {
            // Not authenticated, redirect to login page            String redirectUrl = LOGIN_PAGE + "?redirect=" + 
                                (().toString(), "UTF-8");
            (redirectUrl);
        }
    }
    
    private boolean isPublicPath(String path) {
        return ("/public/") || 
               ("/resources/") || 
               ("/error");
    }
}
  • Configure in-app WebMVC
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private SessionAuthenticationFilter sessionAuthenticationFilter;
    
    @Bean
    public FilterRegistrationBean&lt;SessionAuthenticationFilter&gt; sessionFilterRegistration() {
        FilterRegistrationBean&lt;SessionAuthenticationFilter&gt; registration = new FilterRegistrationBean&lt;&gt;();
        (sessionAuthenticationFilter);
        ("/*");
        (Ordered.HIGHEST_PRECEDENCE + 1); // Execute after Spring Security        return registration;
    }
}
  • Use session information
@Controller
@RequestMapping("/dashboard")
public class DashboardController {

    @GetMapping
    public String dashboard(HttpSession session, Model model) {
        UserInfo userInfo = (UserInfo) ("USER_INFO");
        
        ("user", userInfo);
        
        return "dashboard";
    }
}

Pros and cons

advantage:

  • Simple to implement and easy to understand
  • Seamless integration with Spring Eco
  • Sessions can contain rich information

shortcoming:

  • Rely on central storage (such as Redis)
  • Session data needs to be serialized/deserialized
  • Relying on cookies is not suitable for non-web applications

5. Plan selection and best practices

Select a suggestion

Program Type Recommended scenarios Not suitable for scenes
Cookie-Session Small applications under the same domain name, simple authentication requirements Cross-domain applications, mobile application integration, high security requirements
JWT Distributed microservices, front-end and back-end separation applications Scenarios where tokens need to be revoked instantly, with extremely high security requirements
OAuth 2.0/OIDC Enterprise-level applications, requiring third-party integration, multi-tenant system Small applications, resource-constrained environment, rapid development needs
Spring Session Application of Spring Technology Stack, Application of Medium Enterprises Heterogeneous technology stack, non-Web application integration

6. Summary

From a simple Cookie-Session-based solution to a complex OAuth 2.0/OIDC implementation, the choice of SSO solution should be comprehensively considered based on business needs, security requirements, user experience goals and technical constraints.

The above is the detailed content of the four solutions for SpringBoot to implement single sign-on (SSO). For more information about SpringBoot single sign-on SSO, please follow my other related articles!