关于SpringSecurity授权

339 阅读3分钟

HTTP请求授权

即 授权ServerHttpRequest

Spring Security提供了对传入HTTP请求的授权支持。默认情况下,Spring Security的授权将要求所有请求都经过身份验证。显式配置如下:

所有请求都需要认证:
package com.hz.ss.config;
​
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
​
import static org.springframework.security.config.Customizer.withDefaults;
​
/**
 * @author Dong
 * @version 1.0
 * @date 2022/5/5
 */
@Configuration
public class SecurityConfig {
    @Bean
    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
            .httpBasic(withDefaults())
            .formLogin(withDefaults());
        return http.build();
    }
}
自定义认证规则

我们可以通过按优先顺序添加更多规则来配置Spring Security,使其具有不同的规则。

package com.hz.ss.config;
​
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
​
import static org.springframework.security.config.Customizer.withDefaults;
​
/**
 * @author Dong
 * @version 1.0
 * @date 2022/5/5
 */
@Configuration
public class SecurityConfig {
    @Bean
    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange((authorize) -> authorize // 0
                       .pathMatchers("/resources/**", "/signup", "/about").permitAll()//1
                       .pathMatchers("/admin/**").hasRole("ADMIN")//2
                       .pathMatchers("/db/**").access((authentication, context) -> // 3
                                 hasRole("ADMIN")
                                .check(authentication, context)
                                .filter(decision -> !decision.isGranted())
                                .switchIfEmpty(hasRole("DBA").check(authentication, context))
                        )
                        .anyExchange().denyAll() //4
                );
        return http.build();
    }
}
  1. 指定了多个授权规则。每个规则都是按照它们声明的顺序考虑的。
  2. 我们指定了多个任何用户都可以访问的URL模式。具体来说,如果URL以“/resources/”开头,等于“/signup”或等于“/about”,任何用户都可以访问请求。
  3. 任何以“/admin/”开头的URL将被限制为具有“ROLE_ADMIN”权限的用户。您会注意到,由于我们正在调用hasRole方法,所以不需要指定“ROLE_”前缀。
  4. 任何以“/db/”开头的URL都要求用户同时拥有“ROLE_ADMIN”和“ROLE_DBA”。这展示了提供自定义ReactiveAuthorizationManager的灵活性,它允许我们实现任意授权逻辑。为简单起见,示例使用lambda和委托到现有的AuthorityReactiveAuthorizationManager.hasRole实现。然而,在实际情况中,应用程序可能会在实现ReactiveAuthorizationManager的适当类中实现逻辑。
  5. 任何未匹配的URL都将被拒绝访问。如果不想意外地忘记更新授权规则,这是一个很好的策略。

说明:在自定义认证规则定义好之后,我们可以在浏览器中输入对应的URL,可以观察,http://127.0.0.1:8080/resources 可直接访问;http://127.0.0.1:8080/admin 需要输入账号进行认证,且具有ADMIN角色。

EnableReactiveMethodSecurity [开启响应式方法安全]

Spring Security使用 Reactor’s Context 来支持方法安全,这个Context是用ReactiveSecurityContextHolder来设置的。例如,这将演示如何检索当前登录用户的消息。

为此,该方法的返回类型必须是org.reactivestreams.Publisher(即Mono/Flux),或者该函数必须是Kotlin协程函数。这对于与Reactor's Context集成是必要的。

最小方法安全性配置

下面是在响应式应用程序中使用方法安全性时的最小方法安全性配置。

package com.hz.ss.config;
​
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
​
/**
 * @author Dong
 * @version 1.0
 * @date 2022/5/5
 */
@EnableReactiveMethodSecurity
public class ReactiveMethodSecurityConfig {
    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
        UserDetails rob = userBuilder.username("rob")
                .password("rob")
                .roles("USER")
                .build();
        UserDetails admin = userBuilder.username("admin")
                .password("admin")
                .roles("USER","ADMIN")
                .build();
        return new MapReactiveUserDetailsService(rob, admin);
    }
}
创建一个 Service
package com.hz.ss.service;
​
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
​
/**
 * @author Dong
 * @version 1.0
 * @date 2022/5/5
 */
@Service
public class HelloWorldMessageService {
    @PreAuthorize("hasRole('ADMIN')")
    public Mono<String> findMessage() {
        return Mono.just("Hello World!");
    }
}

结合上面的配置,@PreAuthorize("hasRole('ADMIN')")将确保findMessage只由角色为ADMIN的用户调用。需要注意的是,标准方法安全中的任何表达式都适用于@EnableReactiveMethodSecurity。但是,现在我们只支持返回类型为Boolean或的boolean的表达式。这意味着表达式不能被阻塞。

创建一个Controller
package com.hz.ss.controller;
​
import com.hz.ss.service.HelloWorldMessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
​
/**
 * @author Dong
 * @version 1.0
 * @date 2022/5/5
 */
@RestController
public class HelloWorldMessageController {
​
    @Autowired
    public HelloWorldMessageService service;
​
    @GetMapping("/hello")
    public Mono<String> hello() {
        return service.findMessage();
    }
}
验证

启动上面程序,在浏览器中输入 http://127.0.0.1:8080/hello ,此时自动跳转到登录页面,我们输入 admin/admin ,页面效果如下

image-20220505145037911.png 我们注销账号 http://127.0.0.1:8080/logout 后重新访问 http://127.0.0.1:8080/hello 跳转到登录页后,我们这次输入 rob/rob,页面效果如下

image-20220505145231975.png 即返回拒绝访问