Spring Boot实现单点登录

什么是单点登录

单点登录(Single Sign-On,简称SSO)是一种让用户只需登录一次,就能访问多个应用系统的认证技术。用户只需要使用一组凭证(用户名和密码)登录系统,然后就可以无需再次输入凭证,在其他受信任的系统中自动登录。

Spring Boot单点登录实现方案

在Spring Boot中实现单点登录有多种方案可供选择,其中一种常用的方案是使用基于Token的认证机制。下面我们将使用Spring Security和JSON Web Token(JWT)来实现单点登录。

1. 创建Spring Boot项目

首先,我们需要创建一个Spring Boot项目。你可以使用Spring Initializer( init`命令进行创建。

2. 添加依赖

pom.xml文件中添加以下依赖:

<dependencies>
    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- JSON Web Token -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.2</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.2</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

3. 配置Spring Security

application.properties文件中添加以下配置:

spring.security.user.name=admin
spring.security.user.password=admin

创建一个WebSecurityConfig类,继承WebSecurityConfigurerAdapter,并添加@EnableWebSecurity注解。在该类中,我们将配置Spring Security,包括允许对某些URL进行匿名访问和配置基于JWT的认证策略。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/authenticate").permitAll()
                .anyRequest().authenticated()
                .and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password("$2a$10$Hmz2NtTzJ4O4KZBCSjIX3eYVwJ4T9rP1iFYjOojjGyM1zJt1b6UvW")
                .roles("ADMIN");
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}

4. 创建JWT工具类

创建一个JwtUtil类,用于处理JWT的生成、解析和验证。

@Component
public class JwtUtil {

    private final String SECRET_KEY = "secret";

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder().setClaims(claims).setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private