Spring 安全 401 在不安全端点上未经授权
Spring security 401 Unauthorized on unsecured endpoint
我正尝试在 Spring 引导应用程序上配置 Spring 安全性,如下所示:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestAuthenticationEntryPoint unauthorizedHandler;
@Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationFilter authenticationTokenFilter = new JwtAuthenticationFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//@formatter:off
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(this.unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login", "/singup", "/subscribers").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
//@formatter:on
}
}
我的 unauthorizedHandler 是:
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger LOGGER = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
最后,/subscribers 的 REST 控制器是:
@RestController
public class SubscriberRestController {
@Autowired
ISubscribersService subscribersService;
@RequestMapping(value = RequestMappingConstants.SUBSCRIBERS, method = RequestMethod.GET)
@ResponseBody
public Number subscriberCount() {
return subscribersService.subscribersCount();
}
@RequestMapping(value = RequestMappingConstants.SUBSCRIBERS, method = RequestMethod.POST)
public String subscriberPost(@RequestBody SubscriberDocument subscriberDocument) {
return subscribersService.subscribersInsert(subscriberDocument);
}
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return "This is a test";
}
}
我使用 postman 测试端点,当我执行 POST 到 "localhost:8080/subscribers" 时,我得到:
我想打开没有任何安全控制或凭据检查的端点(/订阅者),用于注册和登录的端点以及用于经过身份验证的用户的安全端点。
谢谢! :)
经过一番研究,这里是解决方案:
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
@ComponentScan(basePackages = { PackageConstants.PACKAGE_CONTROLLERS_REST, PackageConstants.PACKAGE_SERVICES,
PackageConstants.PACKAGE_SERVICES_IMPL, PackageConstants.PACKAGE_MONGO_REPOSITORIES,
PackageConstants.PACKAGE_MONGO_REPOSITORIES_IMPL, PackageConstants.PACKAGE_UTILS })
public class Application {
// Clase principal que se ejecuta en el bootrun
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
主线是 @SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
它告诉我们不要使用 Spring 引导安全自动配置配置。这不是完整的答案,因为现在你必须告诉 Spring 用户你的 Spring 安全配置 class。我还建议您使用 init Root Config 类 创建 Initializer class,使用 ApplicationConfiguration 并拒绝使用 SpringBoot 应用程序。像这样:
应用程序配置:
@Configuration
@EnableWebMvc
@ComponentScan("com.trueport.*")
@PropertySource("classpath:app.properties")
public class ApplicationConfig extends WebMvcConfigurerAdapter {
....
}
应用程序安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
....
}
初始化程序:
public class Initializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
....
DispatcherServlet dispatcherServlet = new DispatcherServlet(ctx);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ctx.register(ApplicationConfig.class);
ServletRegistration.Dynamic servlet = servletContext.addServlet(DISPATCHER_SERVLET_NAME,
dispatcherServlet);
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);
}
}
Spring 引导未应用配置,因为找不到它。 Application.java 配置包未包含在 @ComponentScan
注释中。
您需要将以下内容添加到您的配置方法中 /error 是应用程序由于任何异常而发生错误时的默认回退,并且默认情况下是安全的。
protected void configure(HttpSecurity httpSecurity) throws Exception {
//disable CRSF
httpSecurity
//no authentication needed for these context paths
.authorizeRequests()
.antMatchers("/error").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/your Urls that dosen't need security/**").permitAll()
还有下面的代码片段
@Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers("/error/**")
}
现在,当 permitAll Urls 发生异常时,您将不会得到 401 和 500 异常详细信息
如果您的应用程序只是简单地保存 API,并且您包含了 spring 安全性的依赖项 - 出于任何其他原因(我的是启用 headers X-Frame 和 Content-Security-Policy),那么默认情况下 Spring 包括用于 csrf 保护的 servlet 过滤器。如果不禁用此功能,所有请求都会失败并出现 HTTP 401 错误。
要禁用它,您创建一个配置 class 扩展 WebSecurityConfigurerAdapter
并用 EnableWebSecurity
注释
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
}
}
这篇 article 值得一读 - 非常详细。
我正尝试在 Spring 引导应用程序上配置 Spring 安全性,如下所示:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestAuthenticationEntryPoint unauthorizedHandler;
@Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationFilter authenticationTokenFilter = new JwtAuthenticationFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//@formatter:off
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(this.unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login", "/singup", "/subscribers").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
//@formatter:on
}
}
我的 unauthorizedHandler 是:
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger LOGGER = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
最后,/subscribers 的 REST 控制器是:
@RestController
public class SubscriberRestController {
@Autowired
ISubscribersService subscribersService;
@RequestMapping(value = RequestMappingConstants.SUBSCRIBERS, method = RequestMethod.GET)
@ResponseBody
public Number subscriberCount() {
return subscribersService.subscribersCount();
}
@RequestMapping(value = RequestMappingConstants.SUBSCRIBERS, method = RequestMethod.POST)
public String subscriberPost(@RequestBody SubscriberDocument subscriberDocument) {
return subscribersService.subscribersInsert(subscriberDocument);
}
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return "This is a test";
}
}
我使用 postman 测试端点,当我执行 POST 到 "localhost:8080/subscribers" 时,我得到:
我想打开没有任何安全控制或凭据检查的端点(/订阅者),用于注册和登录的端点以及用于经过身份验证的用户的安全端点。
谢谢! :)
经过一番研究,这里是解决方案:
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
@ComponentScan(basePackages = { PackageConstants.PACKAGE_CONTROLLERS_REST, PackageConstants.PACKAGE_SERVICES,
PackageConstants.PACKAGE_SERVICES_IMPL, PackageConstants.PACKAGE_MONGO_REPOSITORIES,
PackageConstants.PACKAGE_MONGO_REPOSITORIES_IMPL, PackageConstants.PACKAGE_UTILS })
public class Application {
// Clase principal que se ejecuta en el bootrun
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
主线是 @SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
它告诉我们不要使用 Spring 引导安全自动配置配置。这不是完整的答案,因为现在你必须告诉 Spring 用户你的 Spring 安全配置 class。我还建议您使用 init Root Config 类 创建 Initializer class,使用 ApplicationConfiguration 并拒绝使用 SpringBoot 应用程序。像这样:
应用程序配置:
@Configuration
@EnableWebMvc
@ComponentScan("com.trueport.*")
@PropertySource("classpath:app.properties")
public class ApplicationConfig extends WebMvcConfigurerAdapter {
....
}
应用程序安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
....
}
初始化程序:
public class Initializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
....
DispatcherServlet dispatcherServlet = new DispatcherServlet(ctx);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ctx.register(ApplicationConfig.class);
ServletRegistration.Dynamic servlet = servletContext.addServlet(DISPATCHER_SERVLET_NAME,
dispatcherServlet);
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);
}
}
Spring 引导未应用配置,因为找不到它。 Application.java 配置包未包含在 @ComponentScan
注释中。
您需要将以下内容添加到您的配置方法中 /error 是应用程序由于任何异常而发生错误时的默认回退,并且默认情况下是安全的。
protected void configure(HttpSecurity httpSecurity) throws Exception {
//disable CRSF
httpSecurity
//no authentication needed for these context paths
.authorizeRequests()
.antMatchers("/error").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/your Urls that dosen't need security/**").permitAll()
还有下面的代码片段
@Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers("/error/**")
}
现在,当 permitAll Urls 发生异常时,您将不会得到 401 和 500 异常详细信息
如果您的应用程序只是简单地保存 API,并且您包含了 spring 安全性的依赖项 - 出于任何其他原因(我的是启用 headers X-Frame 和 Content-Security-Policy),那么默认情况下 Spring 包括用于 csrf 保护的 servlet 过滤器。如果不禁用此功能,所有请求都会失败并出现 HTTP 401 错误。
要禁用它,您创建一个配置 class 扩展 WebSecurityConfigurerAdapter
并用 EnableWebSecurity
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
}
}
这篇 article 值得一读 - 非常详细。