标题:《跨域设置 Cookie 实现单点登录的原理与实践》
一、引言
在当今的网络应用中,单点登录(Single Sign-On,SSO)成为了一种常见的需求,它允许用户在多个相关的应用系统中只需进行一次登录,即可访问其他系统,而无需再次输入用户名和密码,而跨域设置 Cookie 则是实现单点登录的一种重要技术手段,本文将详细介绍跨域设置 Cookie 的原理,并通过实际案例展示如何在不同的应用场景中实现单点登录。
二、跨域设置 Cookie 的原理
(一)同源策略
在浏览器中,同源策略是一种安全机制,它限制了不同源之间的资源访问,同源是指协议、域名和端口号都相同,如果两个页面的源不同,那么它们之间无法直接访问对方的 Cookie、LocalStorage 和 SessionStorage 等本地存储对象。
(二)跨域请求
当一个页面向另一个不同源的服务器发送请求时,浏览器会自动发起一个跨域请求,跨域请求会携带一些额外的头信息,如 Origin、Referer 等,以告知服务器请求的来源。
(三)设置 Cookie 的条件
要在跨域请求中设置 Cookie,需要满足以下条件:
1、目标服务器允许跨域设置 Cookie。
2、响应头中包含 Set-Cookie 字段,并且设置了 SameSite 属性为 None 或 Lax。
3、浏览器支持跨域设置 Cookie。
(四)SameSite 属性
SameSite 属性是 Cookie 的一个新属性,它用于控制 Cookie 在跨域请求中的发送方式,SameSite 属性有三个值:
1、None:表示 Cookie 可以在跨域请求中发送。
2、Lax:表示 Cookie 在以下情况下可以在跨域请求中发送:
- 同源页面之间的链接点击。
- 表单提交。
- 预加载请求。
3、Strict:表示 Cookie 只能在同源页面之间发送,不能在跨域请求中发送。
三、跨域设置 Cookie 实现单点登录的流程
(一)用户登录
用户在登录页面输入用户名和密码,然后点击登录按钮,登录页面将用户信息发送到登录服务器进行验证。
(二)登录服务器验证用户信息
登录服务器验证用户信息是否正确,如果用户信息正确,登录服务器将生成一个会话 ID,并将其设置为 Cookie 发送给客户端。
(三)客户端保存会话 ID
客户端接收到登录服务器发送的 Cookie 后,将其保存到本地存储中。
(四)用户访问其他应用系统
用户在访问其他应用系统时,客户端会自动携带登录服务器生成的会话 ID 发送到应用服务器。
(五)应用服务器验证会话 ID
应用服务器接收到客户端发送的会话 ID 后,将其发送到登录服务器进行验证,如果会话 ID 有效,应用服务器将允许用户访问该应用系统。
四、跨域设置 Cookie 实现单点登录的案例分析
(一)前后端分离项目
在前后端分离项目中,前端和后端通常是不同的源,为了实现单点登录,需要在前后端之间进行协调,以下是一个前后端分离项目中实现单点登录的示例:
1、后端实现
后端使用 Spring Security 框架实现单点登录,在登录成功后,后端将会话 ID 设置为 Cookie 发送给客户端,并在响应头中设置 Set-Cookie 字段,设置 SameSite 属性为 None。
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .successHandler(new SimpleUrlAuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { String username = authentication.getName(); UserDetails userDetails = userDetailsService.loadUserByUsername(username); String token = UUID.randomUUID().toString(); userDetails.setToken(token); userDetailsService.saveUser(userDetails); Cookie cookie = new Cookie("token", token); cookie.setHttpOnly(true); cookie.setPath("/"); cookie.setSameSite(Cookie.SameSite.NONE); response.addCookie(cookie); super.onAuthenticationSuccess(request, response, authentication); } }) .failureHandler(new SimpleUrlAuthenticationFailureHandler()); } @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } }
2、前端实现
前端使用 Axios 库发送请求,并在请求头中携带会话 ID,在登录成功后,前端将会话 ID 保存到本地存储中。
axios.post('/login', { username: 'userzhangsan', password: '123456' }) .then(response => { localStorage.setItem('token', response.data.token); }) .catch(error => { console.log(error); }); axios.get('/userprotected', { headers: { Authorization: localStorage.getItem('token') } }) .then(response => { console.log(response.data); }) .catch(error => { console.log(error); });
(二)微服务架构项目
在微服务架构项目中,每个服务通常是一个独立的源,为了实现单点登录,需要在各个服务之间进行协调,以下是一个微服务架构项目中实现单点登录的示例:
1、认证服务
认证服务负责用户的认证和授权,在用户登录成功后,认证服务将生成一个会话 ID,并将其设置为 Cookie 发送给客户端,并在响应头中设置 Set-Cookie 字段,设置 SameSite 属性为 None。
@RestController @RequestMapping("/auth") public class AuthController { @Autowired private UserDetailsService userDetailsService; @PostMapping("/login") public String login(@RequestBody LoginRequest loginRequest) { String username = loginRequest.getUsername(); String password = loginRequest.getPassword(); UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (!passwordEncoder.matches(password, userDetails.getPassword())) { return "登录失败"; } String token = UUID.randomUUID().toString(); userDetails.setToken(token); userDetailsService.saveUser(userDetails); Cookie cookie = new Cookie("token", token); cookie.setHttpOnly(true); cookie.setPath("/"); cookie.setSameSite(Cookie.SameSite.NONE); response.addCookie(cookie); return "登录成功"; } }
2、资源服务
资源服务负责提供受保护的资源,在资源服务接收到客户端发送的请求时,资源服务将验证会话 ID 的有效性,如果会话 ID 有效,资源服务将允许客户端访问该资源。
@RestController @RequestMapping("/resource") public class ResourceController { @Autowired private UserDetailsService userDetailsService; @GetMapping("/protected") public String protectedResource() { String token = request.getHeader("Authorization"); UserDetails userDetails = userDetailsService.loadUserByUsername(token); if (userDetails == null) { return "访问被拒绝"; } return "访问成功"; } }
五、总结
跨域设置 Cookie 是实现单点登录的一种重要技术手段,通过设置响应头中的 SameSite 属性为 None 或 Lax,以及在浏览器中支持跨域设置 Cookie,我们可以在不同的源之间共享会话信息,实现单点登录,在实际应用中,我们需要根据具体的情况选择合适的实现方式,并注意安全问题。
评论列表