springboot+spring-security+ajax+前后端分离解决Authorization请求头跨域问题

security+ajax+前后端分离解决Authorization请求头跨域问题

背景

后端采用 springboot+spring-security+oauth2开发
前台采用纯静态页面的形式开发。()
以此做到前后端分离。采用token的形式做无状态登录。为以后业务扩张的分布式扩展做准备。

问题

前后端分离第一个要解决的是跨域问题。为此我再后台起了一个bean

	import org.springframework.context.annotation.Bean;
	import org.springframework.context.annotation.Configuration;
	import org.springframework.web.cors.CorsConfiguration;
	import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
	import org.springframework.web.filter.CorsFilter;

	@Configuration
	public class ConfigurationBeans {

    @Bean
	    public CorsFilter corsFilter() {
	        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
	        final CorsConfiguration corsConfiguration = new CorsConfiguration();
	        corsConfiguration.addAllowedOrigin("*");
	        corsConfiguration.setAllowCredentials(true);
	        corsConfiguration.addAllowedHeader("*");
	        corsConfiguration.addAllowedMethod("*");
	        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
	        return new CorsFilter(urlBasedCorsConfigurationSource);
	    }
	}

sercurity配置也得加上这段代码

protected void configure(HttpSecurity http) throws Exception {
        http.cors()
                .and()
                .csrf()
                .disable();
        //下面是http其它配置项
}

附上ajax代码

	var data = getData();
	var auth = getToken();
	var returnData = null;
	 $.ajax({
      type: api.type,
      url: webDomain + api.url,
      data: data,
      async: false,
      contentType: false,
      processData: false,
      beforeSend : function(request) {
        request.setRequestHeader("Authorization",auth);
      },
      success: function (result) {
        console.log(2)
        if (result.code == 200) {
          returnData = successFunction(result);
        } else {
          alert(result.msg)
        }
      },
      error: function (jqXHR) {
        console.log(jqXHR)
        alert("发生错误:" + jqXHR.status);
      }
    });
  }

但是除了登录等不需要不需要token的接口,调用其它的接口都一脸懵逼的报找不到token异常,为此,我猜想是跨域配置时,我没有允许Authorization请求头放行。也就是上面这句Java代码配置不对:

	corsConfiguration.addAllowedHeader("*");

我开始尝试重新配置这个addAllowedHeader:

 corsConfiguration.addAllowedHeader("Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization");
 corsConfiguration.addAllowedHeader("Authorization,*");
 corsConfiguration.addAllowedHeader("*,Authorization");

无一例外,失败了。重新审视一下问题,有没有可能是springsecurity的过滤器链的优先级比这个跨域过滤器的优先级高?
因为前台小弟等着对接,所以没那么多时间去考虑那么多,就采取了一种简单粗暴的形式去解决。来不及去深究真正原因,直接不采用bean的形式去做跨域处理,而采用实现filter的形式,并通过 Integer.MIN_VALUE 指定优先级最高。


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

@Configuration
@Order(Integer.MIN_VALUE)
@Slf4j
public class CustomCorsFilter implements Filter {

   @Override
   public void destroy() {

   }

   @Override
   public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
           throws IOException, ServletException {

       HttpServletRequest request = (HttpServletRequest) req;
       HttpServletResponse response = (HttpServletResponse) res;
       log.info("Authorization={}",request.getHeader("Authorization"));
       Enumeration<String> es = request.getHeaderNames();
       String s = "";
       while (es.hasMoreElements()){
           s = es.nextElement()+","+s;
       }
       log.info(s);
       response.setHeader("Access-Control-Allow-Origin", "*");
       response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,PUT");
       response.setHeader("Access-Control-Max-Age", "3600");
       response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization");
       chain.doFilter(req, res);

   }

   @Override
   public void init(FilterConfig arg0) throws ServletException {

   }

}
### 单点登录(Single Sign-On, SSO)概述 在前后端分离架构中,单点登录允许用户在一个地方完成认证后能够无缝访问多个相互信任的服务。这不仅提升了用户体验还简化了安全管理和身份验证流程[^3]。 ### 使用Spring Boot与JWT实现SSO的关键要素 为了实现在基于Spring Boot的微服务环境中有效的单点登录机制,通常会采用JSON Web Token (JWT)作为令牌标准来传递经过加密签名的身份信息给前端应用。这种方式可以确保请求的安全性和效率。 #### 配置OAuth2 Server 创建一个专门负责处理认证逻辑的服务实例,该实例需配置为OAuth2授权服务器并支持多种grant types以便适应不同场景下的需求: ```java @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client-id") .secret("{noop}secret") .authorizedGrantTypes("authorization_code", "refresh_token", "password") .scopes("read", "write"); } } ``` 此段代码定义了一个内存中的客户端详情存储器,并指定了所使用的授权模式及其对应的范围权限。 #### JWT Token Enhancer 为了让生成出来的token携带更多自定义的信息,可以通过扩展`JwtAccessTokenConverter`类来自定义Token增强处理器: ```java @Component public class CustomTokenEnhancer implements TokenEnhancer { private final JwtAccessTokenConverter accessTokenConverter; public CustomTokenEnhancer(JwtAccessTokenConverter accessTokenConverter){ this.accessTokenConverter = accessTokenConverter; } @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String,Object> additionalInfo = new HashMap<>(); // Add custom claims here... ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(additionalInfo); return accessToken; } } ``` 这段Java代码展示了如何通过继承`TokenEnhancer`接口向已有的access token添加额外的数据字段。 #### 客户端资源保护 对于每一个受保护的应用程序来说,都需要设置相应的Security Configurations以拦截未经许可的HTTP请求。这里展示的是其中一个可能的例子——Resource Server Configuration: ```java @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Value("${security.oauth2.resource.jwt.key-uri}") private String keyUri; @Bean public RemoteTokenServices remoteTokenService() { RemoteTokenServices service = new RemoteTokenServices(); service.setCheckTokenEndpointUrl(keyUri+"/oauth/check_token"); return service; } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/api/**").authenticated(); } } ``` 上述配置使得只有持有有效Access Token才能成功调用API接口;同时利用RemoteTokenServices组件去远程校验传入的token合法性。 #### 前端集成 当涉及到实际开发时,前端应用程序应当能够在首次加载页面之前先尝试获取当前用户的登录状态。如果发现未处于登录状态下,则重定向至统一入口进行身份验证过程。一旦获得合法凭证(即JWT),则将其保存到本地浏览器缓存或sessionStorage内供后续API交互使用。 ```javascript axios.interceptors.request.use( config => { const token = localStorage.getItem('jwt'); if(token){ config.headers['Authorization'] = `Bearer ${token}`; } return config; }, error => Promise.reject(error) ); // 登录函数示例 function login(username,password){ axios.post('/auth/login', { username, password }) .then(response=>{ const jwt=response.data.token; localStorage.setItem('jwt',jwt); }); } ``` 以上JavaScript片段说明了怎样自动附加Authorization header于每次发出AJAX请求前,并提供了一种简单的模拟登录方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我的孤独与美酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值