配置 Spring 安全性时遇到问题

Having Trouble Configuring Spring Security

我正在尝试使用 Spring 的 Java 配置和注释通过 CXF REST 应用程序配置 Spring 安全性。

我的 WebApplicationInitializer 包含

public class WebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup (ServletContext container) {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);
        container.addListener(new ContextLoaderListener(context));
        context.refresh();

        ServletRegistration.Dynamic cxf = container.addServlet("CXFServlet",new 
CXFServlet());
        cxf.addMapping("/api/*");
        cxf.setLoadOnStartup(1);
    } //onStartup
}

应用配置:

@Configuration
@ComponentScan(basePackages="com.anodyzed.vyta",excludeFilters={
  @ComponentScan.Filter(type=FilterType.ANNOTATION,value=Repository.class)
})
@Import({PersistenceConfig.class,RestConfig.class})
@ImportResource({/*"classpath:applicationContext.xml",*/"classpath:META-INF/cxf/cxf.xml","classpath:META-INF/cxf/cxf-servlet.xml"})
public class AppConfig {

  @Bean
  public CustomerResource customerResource () {
    return new CustomerResource();
  } //CustomerResource

} //*AppConfig

安全配置如下所示:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true)
public class SecurityConfigAdapter extends WebSecurityConfigurerAdapter {

  @Override
  public void configure (AuthenticationManagerBuilder auth) throws Exception {
    PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
    auth.inMemoryAuthentication()
        .withUser("bob").password(encoder.encode("bobpassword"))
        .roles("USER")
      .and()
        .withUser("fred").password(encoder.encode("fredpassword"))
        .roles("ADMIN","USER");
  } //configure

  @Override
  protected void configure (HttpSecurity http) throws Exception {
    http
        .authorizeRequests().antMatchers("/**").authenticated()
        .and().httpBasic()
        .and().csrf().disable();
  } //configure

} //*SecurityConfigAdapter

以及资源本身:

@Path("/customer")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class CustomerResource {

  @Autowired
  private CustomerService customerService;

  @GET
  @Path("/{id}")
  @Secured({"ROLE_USER","ROLE_ADMIN"})
  public Customer read (@PathParam("id") long id) {
    return customerService.read(id);
  } //read

} //*CustomerResource

当我点击 {{server}}/app/api/customer/123(使用 PostMan)时,它 returns 401 Unauthorized。日志显示:

[2018-12-09 21:43:33,307] {resin-port-80-17} DEBUG org.springframework.security.web.FilterChainProxy - /api/customer/123 at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
[2018-12-09 21:43:33,321] {resin-port-80-17} DEBUG org.springframework.security.web.FilterChainProxy - /api/customer/123 at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
[2018-12-09 21:43:33,330] {resin-port-80-17} DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists
[2018-12-09 21:43:33,330] {resin-port-80-17} DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
[2018-12-09 21:43:33,334] {resin-port-80-17} DEBUG org.springframework.security.web.FilterChainProxy - /api/customer/123 at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.FilterChainProxy - /api/customer/123 at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', GET]
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/api/customer/123'; against '/logout'
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', POST]
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /api/customer/123' doesn't match 'POST /logout
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', PUT]
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /api/customer/123' doesn't match 'PUT /logout
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', DELETE]
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /api/customer/123' doesn't match 'DELETE /logout
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.util.matcher.OrRequestMatcher - No matches found
[2018-12-09 21:43:33,335] {resin-port-80-17} DEBUG org.springframework.security.web.FilterChainProxy - /api/customer/123 at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
[2018-12-09 21:43:33,336] {resin-port-80-17} DEBUG org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Basic Authentication Authorization header found for user 'bob'
[2018-12-09 21:43:33,337] {resin-port-80-17} DEBUG org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
[2018-12-09 21:43:33,491] {resin-port-80-17} DEBUG org.springframework.security.authentication.dao.DaoAuthenticationProvider - Authentication failed: password does not match stored value
[2018-12-09 21:43:33,491] {resin-port-80-17} DEBUG org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
[2018-12-09 21:43:33,491] {resin-port-80-17} DEBUG org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint - Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]
[2018-12-09 21:43:33,491] {resin-port-80-17} DEBUG org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint - No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint@1b289b8d
[2018-12-09 21:43:33,492] {resin-port-80-17} DEBUG org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@35870e55
[2018-12-09 21:43:33,492] {resin-port-80-17} DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
[2018-12-09 21:43:33,495] {resin-port-80-17} DEBUG org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed

如能指出我做错的地方,将不胜感激。

对于基本请求,您应该将{username:password} base64encode 到header。 对于用户 bob/bobpassword(base64 在线 here

base64encode{bob:bobpassword} --> Ym9iOmJvYnBhc3N3b3Jk

然后就可以在授权header中发送一个带有basic Ym9iOmJvYnBhc3N3b3Jk的请求了。