集成

Servlet

Spring Security与Servlet API集成在一起

Servlet 2.5+

HttpServletRequest.getRemoteUser()将返回SecurityContextHolder.getContext().getAuthentication().getName()的结果。该结果通常是当前用户名, 检查此属性是否为null可以用来指示用户是否已通过身份验证或匿名

HttpServletRequest.getUserPrincipal()将返回SecurityContextHolder.getContext().getAuthentication() . 这意味着它是一种Authentication

HttpServletRequest.isUserInRole(String)将确定SecurityContextHolder.getContext().getAuthentication().getAuthorities()包含GrantedAuthority,如:boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");

Servlet 3+

HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)方法可用于确保对用户进行身份验证. 如果未通过身份验证,则将使用配置的AuthenticationEntryPoint来请求用户进行身份验证

HttpServletRequest.login(String,String)可用于使用当前AuthenticationManager用户

HttpServletRequest.logout()注销当前用户

AsyncContext.start(Runnable)

确保您的凭据将被传播到新线程的AsyncContext.start(Runnable)方法. 使用Spring Security的并发支持,Spring Security重写AsyncContext.start(Runnable)以确保在处理Runnable时使用当前的SecurityContext.

final AsyncContext async = httpServletRequest.startAsync();
async.start(new Runnable() {
    public void run() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        try {
            final HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse();
            asyncResponse.setStatus(HttpServletResponse.SC_OK);
            asyncResponse.getWriter().write(String.valueOf(authentication));
            async.complete();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
});

Servlet 3.1+

HttpServletRequest#changeSessionId()是防止Servlet 3.1及更高版本中的Session Fixation攻击的默认方法.

Spring Data

Spring Security提供了Spring Data集成,该集成允许在查询中引用当前用户. 将用户包括在查询中以支持分页结果不仅有用,而且有必要,因为事后过滤结果将无法扩展.

Concurrency

在大多数环境中,安全性是基于每个Thread存储的. 这意味着当在新Thread上完成工作时, SecurityContext丢失. Spring Security提供了一些基础架构来帮助用户轻松实现这一点. Spring Security提供了用于在多线程环境中使用Spring Security的底层抽象. 实际上,这就是Spring Security与AsyncContext.start(Runnable)和Spring MVC Async Integration集成的基础 .

DelegatingSecurityContextRunnable

Spring Security并发支持中最基本的构建块之一是DelegatingSecurityContextRunnable . 它包装了一个委托Runnable ,以便使用为委托指定的SecurityContext初始化SecurityContextHolder . 然后,它将调用委托Runnable,以确保随后清除SecurityContextHolder . DelegatingSecurityContextRunnable看起来像这样:

public void run() {
try {
    SecurityContextHolder.setContext(securityContext);
    delegate.run();
} finally {
    SecurityContextHolder.clearContext();
}
}

将当前Thread的SecurityContext转移到调用安全服务的Thread

Runnable originalRunnable = new Runnable() {
public void run() {
    // invoke secured service
}
};

SecurityContext context = SecurityContextHolder.getContext();
DelegatingSecurityContextRunnable wrappedRunnable =
    new DelegatingSecurityContextRunnable(originalRunnable, context);

new Thread(wrappedRunnable).start();

DelegatingSecurityContextExecutor

设计DelegatingSecurityContextExecutor是非常相似的DelegatingSecurityContextRunnable除了它接受委托Executor ,而不是一个委托Runnable

SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
    new UsernamePasswordAuthenticationToken("user","doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER"));
context.setAuthentication(authentication);

SimpleAsyncTaskExecutor delegateExecutor =
    new SimpleAsyncTaskExecutor();
DelegatingSecurityContextExecutor executor =
    new DelegatingSecurityContextExecutor(delegateExecutor, context);

Runnable originalRunnable = new Runnable() {
public void run() {
    // invoke secured service
}
};

executor.execute(originalRunnable);

Spring Security Concurrency Classes

  • DelegatingSecurityContextCallable
  • DelegatingSecurityContextExecutor
  • DelegatingSecurityContextExecutorService
  • DelegatingSecurityContextRunnable
  • DelegatingSecurityContextScheduledExecutorService
  • DelegatingSecurityContextSchedulingTaskExecutor
  • DelegatingSecurityContextAsyncTaskExecutor
  • DelegatingSecurityContextTaskExecutor
  • DelegatingSecurityContextTaskScheduler

Jackson

ObjectMapper mapper = new ObjectMapper();
ClassLoader loader = getClass().getClassLoader();
List<Module> modules = SecurityJackson2Modules.getModules(loader);
mapper.registerModules(modules);

// ... use ObjectMapper as normally ...
SecurityContext context = new SecurityContextImpl();
// ...
String json = mapper.writeValueAsString(context);

Localization

Spring Security支持本地化最终用户可能会看到的异常消息.

Spring MVC

@EnableWebSecurity

MvcRequestMatcher

Spring Security提供了与Spring MVC如何使用MvcRequestMatcher在URL上进行匹配的深度集成. 这有助于确保您的安全规则与用于处理请求的逻辑相匹配.

protected configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests(authorize -> authorize
            .mvcMatchers("/admin").hasRole("ADMIN")
        );
}

@AuthenticationPrincipal

Spring Security提供了AuthenticationPrincipalArgumentResolver ,它可以自动为Spring MVC参数解析当前的Authentication.getPrincipal() .

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser() {
    Authentication authentication =
    SecurityContextHolder.getContext().getAuthentication();
    CustomUser custom = (CustomUser) authentication == null ? null : authentication.getPrincipal();

    // .. find messages for this user and return them ...
}

我们可以使用SpEL表达式访问CustomUser ,该表达式使用Authentication.getPrincipal()作为根对象

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal(expression = "customUser") CustomUser customUser) {

    // .. find messages for this user and return them ...
}

还可以在SpEL表达式中引用Bean. 例如,如果我们使用JPA来管理用户,并且想要修改并保存当前用户的属性

@PutMapping("/users/self")
public ModelAndView updateName(@AuthenticationPrincipal(expression = "@jpaEntityManager.merge(#this)") CustomUser attachedCustomUser,
        @RequestParam String firstName) {

    // change the firstName on an attached instance which will be persisted to the database
    attachedCustomUser.setFirstName(firstName);

    // ...
}

通过使@AuthenticationPrincipal成为我们自己的注释的元注释,我们可以进一步消除对Spring Security的依赖.

@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {}
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@CurrentUser CustomUser customUser) {

    // .. find messages for this user and return them ...
}

Spring MVC Async

Spring Web MVC 3.2+对异步请求处理提供了出色的支持. 如果没有其他配置,Spring Security会自动将SecurityContext设置为执行由您的控制器返回的Callable的Thread

Spring MVC and CSRF Integration

Spring Security提供了CsrfTokenArgumentResolver ,它可以自动为Spring MVC参数解析当前的CsrfToken . 通过使用@EnableWebSecurity,您将自动将其添加到Spring MVC配置中

@RestController
public class CsrfController {

    @RequestMapping("/csrf")
    public CsrfToken csrf(CsrfToken token) {
        return token;
    }
}

WebSocket Security

CORS

Spring Framework 为CORS提供了一流的支持 . 必须在Spring Security之前处理CORS,因为飞行前请求将不包含任何cookie(即JSESSIONID ). 如果请求不包含任何cookie,并且首先使用Spring Security,则该请求将确定用户未通过身份验证(因为请求中没有cookie),并拒绝该用户.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // by default uses a Bean by the name of corsConfigurationSource
            .cors(withDefaults())
            ...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

JSP Tag Libraries

Spring Security有自己的taglib,它为访问安全信息和在JSP中应用安全约束提供了基本的支持.

Updated: