本文目录导读:

我来为您提供一个完整的Java实现SSO单点登录的案例,这个案例将涵盖核心概念、实现方式和完整代码。
SSO单点登录架构
核心组件:
- 认证中心(SSO Server):负责用户认证和票据管理
- 业务系统(Client):需要登录的业务系统
- 认证协议:自定义实现的简单SSO协议
核心实现代码
认证中心实现(SSO Server)
// TokenManager.java - 令牌管理器
@Component
public class TokenManager {
// 使用ConcurrentHashMap存储有效token
private static ConcurrentHashMap<String, Token> tokenStore = new ConcurrentHashMap<>();
// 使用Set存储已登录用户
private static ConcurrentHashMap<String, Set<String>> userTokens = new ConcurrentHashMap<>();
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 生成token
public String generateToken(String username) {
String token = UUID.randomUUID().toString().replace("-", "");
Token tokenObj = new Token();
tokenObj.setToken(token);
tokenObj.setUsername(username);
tokenObj.setCreateTime(new Date());
tokenObj.setExpireTime(new Date(System.currentTimeMillis() + 30 * 60 * 1000)); // 30分钟过期
// 保存到Redis
redisTemplate.opsForValue().set("token:" + token, tokenObj, 30, TimeUnit.MINUTES);
// 记录用户的所有token
Set<String> tokens = userTokens.getOrDefault(username, new HashSet<>());
tokens.add(token);
userTokens.put(username, tokens);
return token;
}
// 校验token
public boolean validateToken(String token) {
Token tokenObj = (Token) redisTemplate.opsForValue().get("token:" + token);
if (tokenObj == null) {
return false;
}
// 检查是否过期
return tokenObj.getExpireTime().getTime() > System.currentTimeMillis();
}
// 获取token对应的用户名
public String getUsername(String token) {
Token tokenObj = (Token) redisTemplate.opsForValue().get("token:" + token);
return tokenObj != null ? tokenObj.getUsername() : null;
}
// 删除token(退出登录)
public void removeToken(String token) {
Token tokenObj = (Token) redisTemplate.opsForValue().get("token:" + token);
if (tokenObj != null) {
redisTemplate.delete("token:" + token);
if (userTokens.containsKey(tokenObj.getUsername())) {
userTokens.get(tokenObj.getUsername()).remove(token);
}
}
}
}
// AuthController.java - 认证中心控制器
@RestController
@RequestMapping("/sso")
public class AuthController {
@Autowired
private UserService userService;
@Autowired
private TokenManager tokenManager;
/**
* 登录接口
*/
@PostMapping("/login")
public Result login(@RequestParam String username,
@RequestParam String password,
@RequestParam(required = false) String redirectUrl) {
// 验证用户
User user = userService.validateUser(username, password);
if (user != null) {
// 生成token
String token = tokenManager.generateToken(username);
// 如果提供了回调地址,重定向回业务系统
if (redirectUrl != null && !redirectUrl.isEmpty()) {
return Result.success(new LoginResponse(token, redirectUrl));
}
return Result.success(new LoginResponse(token, null));
}
return Result.error("用户名或密码错误");
}
/**
* 校验token接口
*/
@GetMapping("/validate")
public Result validateToken(String token) {
if (tokenManager.validateToken(token)) {
String username = tokenManager.getUsername(token);
Map<String, Object> data = new HashMap<>();
data.put("valid", true);
data.put("username", username);
return Result.success(data);
}
return Result.error("token无效");
}
/**
* 退出登录接口
*/
@PostMapping("/logout")
public Result logout(@RequestParam String token) {
tokenManager.removeToken(token);
return Result.success("退出成功");
}
}
业务系统实现(Client)
// SSOFilter.java - SSO过滤器(用于拦截需要登录的请求)
@Component
public class SSOFilter implements Filter {
@Autowired
private SSOClientService ssoClientService;
@Value("${sso.server.url}")
private String ssoServerUrl;
@Value("${sso.client.url}")
private String clientBaseUrl;
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession();
// 检查session中是否有token
String token = (String) session.getAttribute("sso_token");
if (token == null) {
// 从请求参数中获取token(可能来自SSO重定向)
token = req.getParameter("token");
if (token != null) {
// 验证token是否有效
if (ssoClientService.validateToken(token)) {
session.setAttribute("sso_token", token);
session.setAttribute("sso_username",
ssoClientService.getUsername(token));
}
}
}
if (token != null && session.getAttribute("sso_token") != null) {
// 已登录,放行
chain.doFilter(request, response);
} else {
// 未登录,重定向到SSO服务器
String redirectUrl = clientBaseUrl + req.getRequestURI();
String ssoLoginUrl = ssoServerUrl + "/sso/login?redirectUrl=" +
URLEncoder.encode(redirectUrl, "UTF-8");
resp.sendRedirect(ssoLoginUrl);
}
}
}
// SSOClientService.java - SSO客户端服务
@Service
public class SSOClientService {
@Value("${sso.server.url}")
private String ssoServerUrl;
private RestTemplate restTemplate = new RestTemplate();
/**
* 校验token是否有效
*/
public boolean validateToken(String token) {
String url = ssoServerUrl + "/sso/validate?token=" + token;
Result result = restTemplate.getForObject(url, Result.class);
return result != null && result.isSuccess();
}
/**
* 获取token对应的用户名
*/
public String getUsername(String token) {
String url = ssoServerUrl + "/sso/validate?token=" + token;
Result result = restTemplate.getForObject(url, Result.class);
if (result != null && result.isSuccess()) {
Map<String, Object> data = result.getData();
return (String) data.get("username");
}
return null;
}
/**
* 退出登录
*/
public void logout(String token) {
String url = ssoServerUrl + "/sso/logout?token=" + token;
restTemplate.postForObject(url, null, Result.class);
}
}
配置类
// SSOClientConfig.java - 客户端配置
@Configuration
public class SSOClientConfig {
@Bean
public FilterRegistrationBean ssoFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new SSOFilter());
registration.addUrlPatterns("/business/*"); // 需要SSO保护的路径
registration.setOrder(1);
return registration;
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
// SSOServerApplication.java - 认证中心启动类
@SpringBootApplication
@EnableCaching
public class SSOServerApplication {
public static void main(String[] args) {
SpringApplication.run(SSOServerApplication.class, args);
}
}
业务系统控制器
// BusinessController.java - 业务系统
@RestController
@RequestMapping("/business")
public class BusinessController {
@Value("${sso.server.url}")
private String ssoServerUrl;
/**
* 业务方法 - 需要登录才能访问
*/
@GetMapping("/data")
public Result getBusinessData(HttpSession session) {
String username = (String) session.getAttribute("sso_username");
if (username == null) {
return Result.error("未登录");
}
// 模拟业务数据
Map<String, Object> data = new HashMap<>();
data.put("username", username);
data.put("businessData", "这里是业务数据");
data.put("currentTime", new Date());
return Result.success(data);
}
/**
* 退出登录
*/
@GetMapping("/logout")
public Result logout(HttpSession session,
HttpServletRequest request) {
String token = (String) session.getAttribute("sso_token");
if (token != null) {
// 调用SSO服务器注销
String url = ssoServerUrl + "/sso/logout?token=" + token;
RestTemplate restTemplate = new RestTemplate();
restTemplate.postForObject(url, null, Result.class);
session.invalidate();
}
return Result.success("退出成功");
}
}
数据结构定义
// Result.java - 统一返回结果
public class Result implements Serializable {
private boolean success;
private String message;
private Object data;
public static Result success(Object data) {
Result result = new Result();
result.setSuccess(true);
result.setMessage("成功");
result.setData(data);
return result;
}
public static Result error(String message) {
Result result = new Result();
result.setSuccess(false);
result.setMessage(message);
return result;
}
// getter/setter省略
}
// Token.java - Token对象
@Data
public class Token implements Serializable {
private String token;
private String username;
private Date createTime;
private Date expireTime;
public boolean isExpired() {
return expireTime.getTime() < System.currentTimeMillis();
}
}
// User.java - 用户实体
@Data
public class User implements Serializable {
private Long id;
private String username;
private String password;
private String email;
private String role;
// 构造函数、getter/setter省略
}
配置文件
# application.yml - 业务系统配置
server:
port: 8081
spring:
application:
name: sso-client
# SSO配置
sso:
server:
url: http://localhost:8080 # SSO认证中心地址
client:
url: http://localhost:8081 # 当前业务系统地址
---
# application.yml - 认证中心配置
server:
port: 8080
spring:
application:
name: sso-server
redis:
host: localhost
port: 6379
使用说明
启动认证中心
# 启动SSO Server (端口8080) mvn spring-boot:run
配置业务系统
- 复制SSOClient代码到你的业务系统中
- 配置application.yml中的SSO服务器地址
- 启动业务系统(端口8081)
测试流程
# 测试登录
curl -X POST "http://localhost:8080/sso/login" \
-d "username=admin&password=123456"
# 测试访问受保护资源(未登录会被重定向到SSO)
curl "http://localhost:8081/business/data"
# 使用token访问
curl "http://localhost:8081/business/data?token=xxxxx"
核心特性
- 集中认证:所有登录都在认证中心完成
- Token管理:使用Redis存储和管理Token
- 会话共享:多个系统共享登录状态
- 单点登出:一次登出,所有系统失效
- 安全可靠:Token过期时间控制,防止恶意攻击
扩展建议
- 使用标准协议:生产环境建议使用OAuth2、CAS或SAML
- 增加安全措施:HTTPS传输、Token加密、刷新Token机制
- 分布式部署:使用Redis集群存储Token
- 监控与日志:记录所有认证事件,便于问题排查
- 权限控制:结合RBAC权限模型,实现细粒度访问控制
这个案例提供了一个完整的SSO实现基础,您可以根据实际需求进行扩展和优化。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播