本文目录导读:
《基于Shiro实现SSO单点登录:原理、代码与实践》
在当今的企业级应用和大型互联网应用中,单点登录(SSO)已经成为一种非常重要的用户认证和授权机制,它允许用户使用一组凭据(如用户名和密码)登录一次,然后访问多个相互信任的应用系统,而无需在每个系统中单独登录,Shiro是一个强大的、易用的Java安全框架,我们将深入探讨如何基于Shiro实现SSO单点登录。
图片来源于网络,如有侵权联系删除
Shiro SSO单点登录原理
(一)Shiro的基本概念
Shiro提供了身份验证(Authentication)、授权(Authorization)、加密(Cryptography)和会话管理(Session Management)等功能,在SSO场景下,身份验证是核心部分,Shiro的身份验证过程主要涉及到Subject(主体,代表当前用户)、Realm(领域,负责从数据源获取用户的身份和权限信息)等概念。
(二)SSO单点登录流程
1、用户首次访问应用A
- 用户请求应用A的受保护资源。
- 应用A发现用户未登录,将用户重定向到SSO服务器的登录页面。
2、SSO服务器登录
- 用户在SSO服务器的登录页面输入用户名和密码。
- SSO服务器的Realm从数据库或其他数据源验证用户的身份信息,如果验证成功,SSO服务器会创建一个全局会话(Global Session),并生成一个唯一的令牌(Token),这个令牌将用于标识用户在整个SSO系统中的身份。
3、重定向回应用A
- SSO服务器将用户重定向回应用A,并在重定向的URL中带上令牌。
- 应用A接收到带有令牌的请求后,会向SSO服务器验证令牌的有效性,如果令牌有效,应用A会创建自己的本地会话(Local Session),并将用户视为已登录状态,允许用户访问受保护的资源。
4、用户访问应用B
- 当用户随后访问应用B时,应用B发现用户未登录,同样将用户重定向到SSO服务器。
- SSO服务器发现用户已经有全局会话(通过令牌识别),直接将用户重定向回应用B,并带上令牌。
- 应用B验证令牌有效后,创建本地会话,用户即可访问应用B的受保护资源。
基于Shiro的SSO单点登录代码实现
(一)项目结构搭建
1、创建一个Maven或Gradle项目。
- 在项目的依赖管理文件(pom.xml或build.gradle)中添加Shiro相关依赖,例如在Maven项目中:
```xml
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro - core</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro - web</artifactId>
<version>1.7.1</version>
</dependency>
```
2、创建多个Web应用(模拟应用A和应用B),可以使用Spring Boot来快速创建Web应用。
(二)SSO服务器端代码
1、配置Shiro的Realm
- 创建一个自定义的Realm类,例如SSORealm
,它继承自AuthorizingRealm
。
```java
public class SSORealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从token中获取用户名
String username = (String) token.getPrincipal();
// 假设从数据库查询用户信息,这里简化为硬编码
if ("admin".equals(username)) {
// 密码使用Shiro的SimpleHash进行加密存储和验证
String hashedPassword = new SimpleHash("md5", "123456", null, 2).toString();
return new SimpleAuthenticationInfo(username, hashedPassword, getName());
}
return null;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 这里可以进行权限查询和设置,暂时简单返回空
return null;
图片来源于网络,如有侵权联系删除
}
}
```
2、配置Shiro的SecurityManager
- 在SSO服务器的配置类中创建SecurityManager
并设置SSORealm
。
```java
@Configuration
public class SSOShiroConfig {
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(new SSORealm());
return securityManager;
}
}
```
3、登录和令牌生成逻辑
- 创建登录控制器,例如SSOLoginController
。
```java
@RestController
public class SSOLoginController {
@Autowired
private SecurityManager securityManager;
@PostMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
Subject subject = new Subject.Builder(securityManager).buildSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
// 生成令牌,这里可以使用UUID等方式生成唯一标识
String tokenValue = UUID.randomUUID().toString();
// 将令牌与用户信息存储在全局会话中(可以使用内存、数据库等方式存储)
// 这里简化为内存存储
Map<String, String> globalSession = new ConcurrentHashMap<>();
globalSession.put(tokenValue, username);
return tokenValue;
} catch (AuthenticationException e) {
return "登录失败";
}
}
}
```
(三)应用A和应用B(客户端)代码
1、配置Shiro与SSO服务器交互
- 在应用A和应用B的Shiro配置类中,配置SecurityManager
和与SSO服务器验证令牌的逻辑。
- 创建一个RemoteTokenValidator
类来验证从SSO服务器获取的令牌。
```java
public class RemoteTokenValidator {
public boolean validate(String token) {
// 这里可以通过HTTP请求等方式向SSO服务器验证令牌的有效性
// 假设简单返回true表示验证成功
return true;
}
图片来源于网络,如有侵权联系删除
}
```
- 在ShiroConfig
类中配置SecurityManager
使用RemoteTokenValidator
。
```java
@Configuration
public class ShiroConfig {
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置自定义的Realm,这里可以是一个简单的Realm只用于处理本地会话相关逻辑
securityManager.setRealm(new LocalRealm());
// 设置令牌验证器
securityManager.setSubjectFactory(new RemoteTokenSubjectFactory(new RemoteTokenValidator()));
return securityManager;
}
}
```
2、处理未登录重定向和登录成功后的逻辑
- 在应用A和应用B的过滤器链中,配置对未登录请求的处理。
- 创建一个SSOFilter
类,继承自OncePerRequestFilter
。
```java
public class SSOFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
// 重定向到SSO服务器的登录页面
response.sendRedirect("http://sso - server/login");
} else {
chain.doFilter(request, response);
}
}
}
```
- 在ShiroConfig
中注册SSOFilter
。
```java
@Bean
public FilterRegistrationBean<SSOFilter> ssoFilterRegistration() {
FilterRegistrationBean<SSOFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new SSOFilter());
registration.addUrlPatterns("/*");
return registration;
}
```
安全性考虑
1、令牌安全
- 令牌在传输过程中应该使用加密传输,例如使用HTTPS协议,防止令牌被窃取。
2、会话管理
- 在SSO服务器和应用的本地会话管理中,需要设置合理的会话过期时间,防止会话被长期占用,同时要处理好会话的并发访问问题。
3、密码安全
- 在Realm中验证用户密码时,应该使用安全的加密算法,如上述代码中的MD5算法可以进一步加盐(Salt)处理,提高密码的安全性。
基于Shiro实现SSO单点登录为企业级应用和多应用系统的用户认证和授权提供了一种高效、安全的解决方案,通过理解Shiro的核心概念和SSO的登录流程,我们可以构建出灵活、可扩展的单点登录系统,在实际应用中,还需要根据具体的业务需求和安全要求,对代码进行进一步的优化和完善,例如集成更多的身份验证方式(如第三方登录)、完善权限管理等功能,持续关注安全漏洞并及时进行修复也是保障系统安全稳定运行的重要环节。
评论列表