黑狐家游戏

sso单点登录跨域,shiro 单点登录 跨域

欧气 3 0

标题:Shiro 单点登录跨域实现原理与实践

一、引言

在当今的企业级应用中,单点登录(Single Sign-On,SSO)是一种常见的安全机制,它允许用户只需登录一次,就可以访问多个相互信任的应用系统,而跨域则是指不同源之间的资源访问限制,在 SSO 中,跨域问题可能会导致用户无法顺利登录到其他应用系统,如何实现 Shiro 单点登录跨域是一个值得探讨的问题。

二、Shiro 单点登录原理

Shiro 是一个强大的安全框架,它提供了认证、授权、会话管理等功能,在 Shiro 单点登录中,通常采用以下步骤:

1、用户在登录页面输入用户名和密码,提交到认证服务器进行认证。

2、认证服务器验证用户信息,如果验证通过,则生成一个会话令牌(Session Token),并将其返回给客户端。

3、客户端将会话令牌存储在本地,并在后续的请求中携带该令牌。

4、应用服务器接收到请求后,从请求中提取会话令牌,并将其传递给 Shiro 进行验证。

5、Shiro 验证会话令牌的有效性,如果验证通过,则允许用户访问该应用系统。

三、跨域问题的产生

在上述 Shiro 单点登录流程中,如果应用系统和认证服务器不在同一个域中,就会产生跨域问题,跨域问题的本质是浏览器的同源策略限制了不同源之间的资源访问,在默认情况下,浏览器不允许跨域访问其他域的资源,除非满足以下条件之一:

1、目标资源使用了 CORS(Cross-Origin Resource Sharing)机制。

2、目标资源设置了 Access-Control-Allow-Origin 响应头。

四、Shiro 单点登录跨域解决方案

为了解决 Shiro 单点登录跨域问题,我们可以采用以下几种方案:

1、使用 CORS 机制:CORS 是一种跨域资源共享机制,它允许浏览器在跨域请求时携带凭证(如 Cookie、Authorization 等),在 Shiro 单点登录中,我们可以在认证服务器和应用系统中都启用 CORS 机制,使得客户端可以在跨域请求时携带会话令牌。

2、在应用系统中设置 Access-Control-Allow-Origin 响应头:另一种解决跨域问题的方法是在应用系统中设置 Access-Control-Allow-Origin 响应头,指定允许访问该资源的源,在 Shiro 单点登录中,我们可以在应用系统的过滤器中设置该响应头,使得客户端可以在跨域请求时携带会话令牌。

3、使用 JSON Web Token(JWT):JWT 是一种轻量级的身份验证机制,它可以在不使用会话的情况下实现单点登录,在 Shiro 单点登录中,我们可以使用 JWT 来生成会话令牌,并在跨域请求中携带该令牌,应用系统可以在接收到请求后,验证 JWT 的有效性,从而实现单点登录。

五、使用 CORS 机制实现 Shiro 单点登录跨域

下面是一个使用 CORS 机制实现 Shiro 单点登录跨域的示例代码:

1、认证服务器

@Configuration
@EnableWebMvc
public class AuthServerConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
             .allowedOrigins("http://www.example.com")
             .allowedMethods("GET", "POST", "PUT", "DELETE")
             .allowedHeaders("*")
             .allowCredentials(true);
    }
}

在上述代码中,我们在认证服务器的配置类中添加了一个 CorsRegistry 对象,用于配置 CORS 规则,我们允许所有源访问认证服务器的所有资源,并允许携带凭证。

2、应用系统

@Configuration
@EnableWebMvc
public class AppServerConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
             .allowedOrigins("http://www.example.com")
             .allowedMethods("GET", "POST", "PUT", "DELETE")
             .allowedHeaders("*")
             .allowCredentials(true);
    }
}

在上述代码中,我们在应用系统的配置类中添加了一个 CorsRegistry 对象,用于配置 CORS 规则,我们允许所有源访问应用系统的所有资源,并允许携带凭证。

3、Shiro 配置

@Configuration
public class ShiroConfig {
    @Bean
    public SubjectFactory<Subject> subjectFactory() {
        return new WebSubjectFactory<>();
    }
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }
    @Bean
    public Realm realm() {
        return new MyRealm();
    }
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        securityManager.setSubjectFactory(subjectFactory());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
}

在上述代码中,我们在 Shiro 配置类中配置了一个 WebSubjectFactory 对象,用于创建 Web 环境下的 Subject 对象,我们还配置了一个 DefaultWebSessionManager 对象,用于管理 Web 环境下的 Session 对象,我们配置了一个 ShiroFilterFactoryBean 对象,用于创建 Shiro 过滤器链。

4、登录页面

<!DOCTYPE html>
<html>
<head>
    <title>登录</title>
</head>
<body>
    <form action="/login" method="post">
        <input type="text" name="username" placeholder="用户名" />
        <input type="password" name="password" placeholder="密码" />
        <input type="submit" value="登录" />
    </form>
</body>
</html>

在上述代码中,我们创建了一个简单的登录页面,用户可以在该页面上输入用户名和密码,并提交到认证服务器进行认证。

5、认证控制器

@Controller
public class AuthController {
    @Autowired
    private UserService userService;
    @PostMapping("/login")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) {
        User user = userService.findByUsername(username);
        if (user == null ||!password.equals(user.getPassword())) {
            model.addAttribute("error", "用户名或密码错误");
            return "login";
        }
        // 生成会话令牌
        String token = UUID.randomUUID().toString();
        // 将会话令牌存储到 Redis 中
        redisTemplate.opsForValue().set(token, user.getId());
        // 将会话令牌设置到 Cookie 中
        Cookie cookie = new Cookie("token", token);
        cookie.setPath("/");
        response.addCookie(cookie);
        return "redirect:/home";
    }
    @GetMapping("/logout")
    public String logout(HttpServletRequest request, HttpServletResponse response) {
        // 从 Cookie 中获取会话令牌
        Cookie[] cookies = request.getCookies();
        if (cookies!= null) {
            for (Cookie cookie : cookies) {
                if ("token".equals(cookie.getName())) {
                    // 从 Redis 中删除会话令牌
                    redisTemplate.delete(cookie.getValue());
                }
            }
        }
        return "redirect:/login";
    }
}

在上述代码中,我们创建了一个 AuthController 控制器,用于处理用户的登录和注销请求,在登录请求中,我们首先根据用户名和密码从数据库中查询用户信息,如果用户信息不存在或密码错误,则返回登录页面并显示错误信息,如果用户信息正确,我们则生成一个会话令牌,并将其存储到 Redis 中,我们将会话令牌设置到 Cookie 中,并将用户重定向到首页,在注销请求中,我们首先从 Cookie 中获取会话令牌,然后从 Redis 中删除该会话令牌。

6、应用控制器

@Controller
public class AppController {
    @GetMapping("/home")
    public String home() {
        return "home";
    }
}

在上述代码中,我们创建了一个 AppController 控制器,用于处理用户的首页请求。

7、首页页面

<!DOCTYPE html>
<html>
<head>
    <title>首页</title>
</head>
<body>
    <h1>欢迎来到首页</h1>
</body>
</html>

在上述代码中,我们创建了一个简单的首页页面,用于展示欢迎信息。

六、总结

通过使用 CORS 机制,我们可以在 Shiro 单点登录中实现跨域访问,在实现过程中,我们需要在认证服务器和应用系统中都启用 CORS 机制,并在 Shiro 配置中设置正确的 CORS 规则,我们还需要在登录页面和应用控制器中处理跨域请求的问题,确保用户能够顺利登录到其他应用系统。

标签: #SSO #单点登录 #跨域 #Shiro

黑狐家游戏
  • 评论列表

留言评论