单点登录是一种身份验证服务,允许用户使用一组凭据登录一次,然后在多个应用程序中访问其他应用程序而无需重新进行身份验证。这样,用户只需一次登录即可访问整个应用生态系统,提高了用户体验并简化了身份验证过程。
在SSO系统中,有一个称为身份提供者(Identity Provider,IdP)的中心服务,负责验证用户身份并生成令牌。其他应用程序(服务提供者,Service Provider,SP)通过令牌来验证用户身份,而无需自己进行身份验证。整个过程基于标准的身份验证协议,如OAuth或OpenID Connect。
在开始编写代码之前,我们需要明确一些基本的准备工作。首先,确保你已经安装了Java和Spring Boot的开发环境。其次,理解基本的Spring Boot和Spring Security概念,因为我们将使用它们来实现SSO。
在我们的系统中,我们将创建一个认证中心作为身份提供者。这个认证中心将负责验证用户的凭据并生成令牌。我们可以使用Spring Security OAuth2来实现这一点。
// Authentication Server Configuration
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client-id")
.secret("client-secret")
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("read", "write")
.redirectUris("http://localhost:8081/login");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
服务提供者是我们系统中的应用程序,它将依赖认证中心进行身份验证。我们将创建一个简单的Spring Boot应用作为服务提供者。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/secured/**")
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.and().oauth2Login();
}
}
在这个配置中,我们指定了受保护的路径,并要求用户进行身份验证。使用oauth2Login()配置,我们告诉Spring Security使用OAuth2进行身份验证。
现在我们已经创建了一个简单的服务提供者,我们可以轻松地集成多个服务提供者。每个服务提供者都将依赖相同的认证中心进行身份验证。
启动认证中心和多个服务提供者,然后访问其中一个服务提供者的受保护路径。系统将重定向到认证中心进行身份验证,一旦验证成功,用户将被重定向回原始的服务提供者,并且在整个过程中只需登录一次。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SsoIntegrationTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testSsoLoginAndAccessProtectedResource() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
// Step 1: Initiate SSO login
ResponseEntity<String> loginResponse = restTemplate.exchange(
"http://localhost:" + port + "/login",
HttpMethod.GET, entity, String.class);
// Assert login page is returned
assertEquals(HttpStatus.OK, loginResponse.getStatusCode());
assertTrue(loginResponse.getBody().contains("Login with"));
// Step 2: Simulate user login (Manual step for testing)
// Step 3: Follow redirect after successful login
HttpHeaders redirectHeaders = new HttpHeaders();
redirectHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> redirectEntity = new HttpEntity<>("parameters", redirectHeaders);
ResponseEntity<String> redirectResponse = restTemplate.exchange(
loginResponse.getHeaders().getLocation(),
HttpMethod.GET, redirectEntity, String.class);
// Assert access to secured resource is successful
assertEquals(HttpStatus.OK, redirectResponse.getStatusCode());
assertTrue(redirectResponse.getBody().contains("Protected Resource"));
}
}
通过本文,我们学习了如何使用Spring Boot框架实现SSO单点登录。我们创建了一个简单的认证中心和一个服务提供者,并集成了多个服务提供者。这样,用户只需一次登录即可访问整个系统,提高了用户体验。