统一身份认证系统
小明:最近我在研究统一身份认证系统,特别是在学院管理系统中,想了解一下如何用Java来实现?
小李:哦,这个问题挺常见的。统一身份认证(SSO)是现在很多系统都需要的功能,尤其是在学院这样的多部门环境中。你打算怎么做呢?
小明:我想先了解什么是SSO,然后看看怎么用Java实现一个简单的例子。
小李:好的,SSO指的是用户只需登录一次,就能访问多个相关系统,不需要重复登录。这在学院中特别有用,比如学生可以同时访问教务系统、图书馆、在线课程等。
小明:明白了。那Java里有哪些常用的框架或库可以用来实现SSO呢?
小李:Java生态中有很多成熟的SSO解决方案,比如Spring Security、OAuth2、JWT、Shiro等。其中,Spring Security是一个非常强大的安全框架,支持多种认证方式,包括基于令牌的认证(如JWT)。
小明:听起来不错。那我们能不能先从一个简单的例子开始?比如一个基于JWT的SSO系统?
小李:当然可以!我们可以设计一个简单的认证流程:用户登录后,服务器生成一个JWT令牌并返回给客户端;之后客户端在每次请求时携带这个令牌,服务器验证令牌的有效性。
小明:那具体的代码结构是什么样的呢?我需要哪些依赖?
小李:我们可以使用Spring Boot来快速搭建项目。首先,添加必要的依赖,比如Spring Web、Spring Security、JWT相关的库。
小明:那具体怎么写呢?有没有示例代码?
小李:好的,下面是一个简单的例子,展示如何用Java实现基于JWT的SSO系统。
小明:太好了,我来看看。
// pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
</dependencies>
小明:嗯,这些依赖看起来没问题。接下来应该怎么做?
小李:接下来我们需要定义一个JWT工具类,用于生成和解析令牌。

// JwtUtil.java
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class JwtUtil {
private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private static final long EXPIRATION_TIME = 86400000; // 24 hours
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SECRET_KEY)
.compact();
}
public static String getUsernameFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}
小明:这个类看起来很实用。那如何在控制器中使用它呢?
小李:我们可以创建一个登录接口,当用户提交用户名和密码后,验证成功后生成JWT令牌并返回。
// AuthController.java
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
// 这里应进行真实的用户验证逻辑
if ("admin".equals(username) && "123456".equals(password)) {
return JwtUtil.generateToken(username);
} else {
throw new RuntimeException("Invalid credentials");
}
}
}
小明:那如何在其他接口中验证令牌呢?
小李:我们可以使用Spring Security的过滤器来拦截请求,并检查请求头中的JWT令牌。
// JwtFilter.java
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtFilter extends OncePerRequestFilter {
private final UserDetailsService userDetailsService;
public JwtFilter(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
String username = JwtUtil.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
小明:那如何将这个过滤器集成到Spring Security中呢?
小李:我们可以在配置类中添加这个过滤器到SecurityFilterChain中。
// SecurityConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtFilter jwtFilter;
public SecurityConfig(JwtFilter jwtFilter) {
this.jwtFilter = jwtFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}

小明:这样就完成了吗?那测试一下吧。
小李:是的,现在你可以通过发送POST请求到/api/auth/login来获取JWT令牌,然后在其他请求中带上Authorization头,格式为Bearer [token]。
小明:那如果我要扩展成学院管理系统的SSO呢?
小李:可以考虑将用户信息存储在数据库中,并通过JWT传递用户角色信息。此外,还可以使用OAuth2来实现更复杂的授权流程。
小明:明白了,这对我理解SSO在学院管理中的应用帮助很大。
小李:没错,SSO不仅能提升用户体验,还能提高系统的安全性。如果你有兴趣,我可以带你深入学习OAuth2和Spring Security的高级用法。
小明:太好了,我期待下一步的学习!