八、匿名访问和跨域

一、匿名访问

新建AnonymousController

  @RestController
  @RequestMapping("/anoy")
  public class AnonymousController {

      @RequestMapping("/hello")
      public Mono<String> hello() {
          return Mono.just("123");
      }
  }

如果想要访问路径匹配/anoy/**这种模式的请求不需要登录即可访问。要怎么做呢?在Spring Reactive Security中可以使用匿名访问。

 @Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
            .authorizeExchange(exchanges -> exchanges
                    .pathMatchers(LOGIN_PAGE).permitAll()
                    .pathMatchers("/verify_code").permitAll()
                    .pathMatchers("/anoy/**").hasRole("ANONYMOUS")
                    .anyExchange().authenticated()
            )
            .formLogin()
            .loginPage(LOGIN_PAGE)
            .and()
            .csrf().disable()
            .anonymous()
            .and()
            .logout()
            ;

    http.addFilterAt(authenticationManager(), SecurityWebFiltersOrder.FORM_LOGIN);
    return http.build();
}

在SecurityWebFilterChain中加入anonymous配置并添加pathMatchers("/anoy/**").hasRole("ANONYMOUS")权限控制。重启后不需要登录即可访问/anoy/hello。但是访问其他资源还是需要登录。

 

SecurityWebFilterChain中的anonymous()是配置AnonymousAuthenticationWebFilter的。可以看下anonymous()默认实现:

	public AnonymousSpec anonymous() {
	    if (this.anonymous == null) {
			this.anonymous = new AnonymousSpec();
		}
		return this.anonymous;
	}

AnonymousAuthenticationWebFilter是由AnonymousSpec配置的,看下属性:

	private String key;

	private AnonymousAuthenticationWebFilter authenticationFilter;

	private Object principal = "anonymousUser";

	private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");

可以看到匿名用户角色是ROLE_ANONYMOUS

AnonymousAuthenticationWebFilter

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
	return ReactiveSecurityContextHolder.getContext().switchIfEmpty(Mono.defer(() -> {
		Authentication authentication = createAuthentication(exchange);
		SecurityContext securityContext = new SecurityContextImpl(authentication);
		logger.debug(LogMessage.format("Populated SecurityContext with anonymous token: '%s'", authentication));
		return chain.filter(exchange)
				.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))
				.then(Mono.empty());
	})).flatMap((securityContext) -> {
		logger.debug(LogMessage.format("SecurityContext contains anonymous token: '%s'",
				securityContext.getAuthentication()));
		return chain.filter(exchange);
	});
}

	protected Authentication createAuthentication(ServerWebExchange exchange) {
	      return new AnonymousAuthenticationToken(this.key, this.principal, this.authorities);
  	}

创建了AnonymousAuthenticationToken。

二、跨域

跨域只存现在前后端分离的项目中。当前端的部署ip或端口与后端的ip或端口不同时就会出现跨域。解决方案就是添加允许跨域的四个响应头。分别是Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Credentials,Access-Control-Allow-Method。一般不会直接设置响应头。而是通过CORS(跨域资源共享)来设置的。

 

单独新建html文件模拟前后端分离:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <title></title>
    
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
    
  </head>
  <body>
    <div id="content"></div>
    
    <button onclick="getData()">点击</button>
  </body>
  
  <script>
     function getData() {
        $.get('http://localhost:8080/anoy/hello',{}, function(res) {
                $('#content').html(res)
            })
     }
  </script>
</html>

在浏览器打开后点击按钮,查看浏览器控制台:
 

 

修改SecurityWebFilterChain:

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
            .authorizeExchange(exchanges -> exchanges
                    .pathMatchers(LOGIN_PAGE).permitAll()
                    .pathMatchers("/verify_code").permitAll()
                    .pathMatchers("/anoy/**").hasRole("ANONYMOUS")
                    .anyExchange().authenticated()
            )
            .formLogin()
            .loginPage(LOGIN_PAGE)
            .and()
            .csrf().disable()
            .anonymous()
            .and()
            .cors()
            .configurationSource((exchange) -> {
                CorsConfiguration corsConfiguration = new CorsConfiguration();
                corsConfiguration.addAllowedOrigin("*");
                corsConfiguration.addAllowedMethod(HttpMethod.POST);
               
                return corsConfiguration;
            })
            .and()
            .logout()

            ;

    http.addFilterAt(authenticationManager(), SecurityWebFiltersOrder.FORM_LOGIN);
    return http.build();
}

增加了cors,addAllowedOrigin("*")表示所有的ip都可以访问,addAllowedMethod(HttpMethod.POST)表示添加了POST方法访问。默认允许GETHEAD方法。因为登录接口是POST方式,必须要添加POST的CorsConfiguration。重启后点击页面按钮:

 

posted @ 2023-06-09 20:48  shigp1  阅读(106)  评论(0编辑  收藏  举报