iframe嵌入三方域名页面无法打开

产生原因

  • Chrome新版本中禁用第三方操作(当前版本84.0.4147.125)

场景重现

  • 业务方域名为 zbj.com,嵌入iframe域名为 z***.la
  • 用户访问时,cookie不属于同一域名,导致不能获取值,一直跳转登录页
    iframe嵌入三方域名页面无法打开_第1张图片

问题分析

  • 项目基于 spring-session 和 spring-security 处理session和授权校验
  • 通过请教前端大佬,可以通过cookie中设置SameSite=None解决(配合secure=true使用)

关键代码

  • spring-security基本用法
/**
 * 身份校验过滤器
 */
public class BossAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public BossAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    public BossAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
        super(requiresAuthenticationRequestMatcher);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        BossAuthenticationToken bat = new BossAuthenticationToken("", "");
        String authCode = request.getParameter("code");
        if (authCode != null && authCode.trim().length() > 0) {
            Map<String, String> authInfo = BossAuthUtil.decode(authCode, this.key);
            String userId = authInfo.get("userId");
            String token = authInfo.get("token");

            bat = new BossAuthenticationToken(userId, token);
            bat.setDetails(authInfo);
        }

        return this.getAuthenticationManager().authenticate(bat);
    }
}
/**
 * 基本认证对象
 */
public class BossAuthenticationToken extends AbstractAuthenticationToken {

  private static final long serialVersionUID = 1L;
  private String userId;
  private String token;

  @Getter
  private volatile long lastCheckTime;

  public BossAuthenticationToken(String userId, String token) {
    this(userId, token, null);
  }

  public BossAuthenticationToken(String userId, String token,
                                 Collection<? extends GrantedAuthority> authorities) {
    super(authorities);
    this.userId = userId;
    this.token = token;
  }

  public BossAuthenticationToken(BossAuthenticationToken token) {
    super(token.getAuthorities());
    this.userId = token.userId;
    this.token = token.token;

    setDetails(token.getDetails());
  }

  //user name or user id
  @Override
  public Object getPrincipal() {
    return this.userId;
  }

  //token or password
  @Override
  public Object getCredentials() {
    return this.token;
  }

  public void setLastCheckTime(long time) {
    synchronized (this) {
      this.lastCheckTime = time;
    }
  }
}
/**
 * 认证中心提供者
 */
@Component
public class BossTokenAuthenticationProvider implements AuthenticationProvider {
  private static final Logger log = LoggerFactory.getLogger(BossTokenAuthenticationProvider.class);

  private UserService userService;

  public BossTokenAuthenticationProvider(UserService userService) {
    this.userService = userService;
  }

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {


    String userId = (String) authentication.getPrincipal();
    String token = (String) authentication.getCredentials();


    if (userId == null) {
      throw new UsernameNotFoundException("User not found.");
    }

    if (token == null) {
      throw new BadCredentialsException("Invalid user.");
    }

    UserVO user = null;

    try {
      user = userService.findUserById(Integer.parseInt(userId));
    } catch (Exception e) {
      log.error("Find user occurs exception: {}", e.getMessage(), e);
    }
    
    // 查询用户是否存在,设置值等
    BossAuthenticationToken bt = new BossAuthenticationToken(userId, token, grantedAuthorities);
    bt.setDetails(authentication.getDetails());
    bt.setAuthenticated(true);
    bt.setLoginUser(user);
    bt.setLastCheckTime(System.currentTimeMillis());
    return bt;
  }

  @Override
  public boolean supports(Class<?> authentication) {
    return authentication == BossAuthenticationToken.class || authentication == UsernamePasswordAuthenticationToken.class;
  }
}
  • spring-session用法
  @Bean(name = "zbjCookie")
  public CookieSerializer createCookieSerializer() {
    DefaultCookieSerializer chasCookieSerializer = new DefaultCookieSerializer();
    chasCookieSerializer.setCookiePath("/");
    chasCookieSerializer.setCookieName("ZBJ_SESSION");
    chasCookieSerializer.setUseBase64Encoding(true);
    // 这种解决只针对浏览器正常访问,对于无痕访问依然存在问题
    chasCookieSerializer.setUseSecureCookie(true);
    chasCookieSerializer.setSameSite("None");
    return chasCookieSerializer;
  }
  • 关键代码
// 如下代码需要配置使用,请查看文档说明
chasCookieSerializer.setUseSecureCookie(true);
chasCookieSerializer.setSameSite("None");
  • 如果项目使用spring-boot 1.* 是没有 setSameSite 方法
  • 可以尝试将 spring-boot 2.1.0 以上
/**
 * Set the value for the {@code SameSite} cookie directive. The default value is
 * {@code Lax}.
 *
 * @param sameSite the SameSite directive value
 * @since 2.1.0
 */
public void setSameSite(String sameSite) {
    this.sameSite = sameSite;
}
  • 如果底层依赖较多,可将 2.1.0 以上版本的源码 org.springframework.session.web.http.DefaultCookieSerializer 拷贝到项目中

问题集锦

  • 经过测试,在无痕模式还是存在循环跳转登录问题,可能因为无痕模式下,***(前端不专一,不敢下结论)
  • 本地测试这个功能,会存在一直轮询问题。因为SameSite搭配scure使用,本地默认不支持https访问
  • 这种方式也可以设置cookie
response.setHeader("Set-Cookie", "SESSION=" + cookie.getValue()+";sameSite=None;secure=true;httpOnly=true");

你可能感兴趣的:(知识总结,Spring,Boot,iframe嵌入,循环登录,认证,会话)