javascript
嵌入式Jetty和Apache CXF:借助Spring Security来保护REST服务
最近,我遇到了一個非常有趣的問題,我認為這只花了我?guī)追昼娋徒鉀Q了:在Windows Server 2003中使用Spring Security (當前穩(wěn)定版本3.2.5 )保護Apache CXF (當前版本3.0.1 )/ JAX-RS REST服務(wù)。在嵌入式Jetty容器(當前版本9.2 )中運行的應(yīng)用程序。 最后,一旦您了解了事物如何協(xié)同工作并了解了細微的內(nèi)在細節(jié),這將變得非常容易。 這篇博客文章將試圖揭示這一點。
我們的示例應(yīng)用程序?qū)⒐_一個簡單的JAX-RS / REST服務(wù)來管理人員。 但是,我們不希望所有人都這樣做,因此需要HTTP基本身份驗證才能訪問部署在http:// localhost:8080 / api / rest / people的端點。 讓我們看一下PeopleRestService類:
package com.example.rs;import javax.json.Json; import javax.json.JsonArray; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces;@Path( "/people" ) public class PeopleRestService {@Produces( { "application/json" } )@GETpublic JsonArray getPeople() {return Json.createArrayBuilder().add( Json.createObjectBuilder().add( "firstName", "Tom" ).add( "lastName", "Tommyknocker" ).add( "email", "a@b.com" ) ).build();} }正如您在上面的代碼片段中所看到的,沒有任何跡象表明該REST服務(wù)是安全的,只是幾個熟悉的JAX-RS批注。
現(xiàn)在,讓我們根據(jù)出色的Spring Security文檔聲明所需的安全配置。 有很多方法可以配置Spring Security,但是我們將展示其中兩種:使用內(nèi)存內(nèi)身份驗證和使用用戶詳細信息服務(wù),這兩種方法都基于WebSecurityConfigurerAdapter構(gòu)建 。 讓我們從內(nèi)存中身份驗證開始,因為它是最簡單的一種:
package com.example.config;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy;@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity( securedEnabled = true ) public class InMemorySecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser( "user" ).password( "password" ).roles( "USER" ).and().withUser( "admin" ).password( "password" ).roles( "USER", "ADMIN" );}@Overrideprotected void configure( HttpSecurity http ) throws Exception {http.httpBasic().and().sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ).and().authorizeRequests().antMatchers("/**").hasRole( "USER" );} }在上面有該段兩個用戶定義: 用戶與角色用戶和管理員與用戶的角色,ADMIN。 我們還通過將授權(quán)策略設(shè)置為僅允許訪問角色為USER的用戶來保護所有URL( / ** )。 作為應(yīng)用程序配置的一部分,讓我們使用@Import批注將其插入AppConfig類。
package com.example.config;import java.util.Arrays;import javax.ws.rs.ext.RuntimeDelegate;import org.apache.cxf.bus.spring.SpringBus; import org.apache.cxf.endpoint.Server; import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; import org.apache.cxf.jaxrs.provider.jsrjsonp.JsrJsonpProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Import;import com.example.rs.JaxRsApiApplication; import com.example.rs.PeopleRestService;@Configuration @Import( InMemorySecurityConfig.class ) public class AppConfig { @Bean( destroyMethod = "shutdown" )public SpringBus cxf() {return new SpringBus();}@Bean @DependsOn ( "cxf" )public Server jaxRsServer() {JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint( jaxRsApiApplication(), JAXRSServerFactoryBean.class );factory.setServiceBeans( Arrays.< Object >asList( peopleRestService() ) );factory.setAddress( factory.getAddress() );factory.setProviders( Arrays.< Object >asList( new JsrJsonpProvider() ) );return factory.create();}@Bean public JaxRsApiApplication jaxRsApiApplication() {return new JaxRsApiApplication();}@Bean public PeopleRestService peopleRestService() {return new PeopleRestService();} }到目前為止,除了最有趣的部分,我們還有所有其他部分:運行嵌入式Jetty實例并創(chuàng)建正確的servlet映射,偵聽器,傳遞我們創(chuàng)建的配置的代碼。
package com.example;import java.util.EnumSet;import javax.servlet.DispatcherType;import org.apache.cxf.transport.servlet.CXFServlet; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.filter.DelegatingFilterProxy;import com.example.config.AppConfig;public class Starter {public static void main( final String[] args ) throws Exception {Server server = new Server( 8080 );// Register and map the dispatcher servletfinal ServletHolder servletHolder = new ServletHolder( new CXFServlet() );final ServletContextHandler context = new ServletContextHandler(); context.setContextPath( "/" );context.addServlet( servletHolder, "/rest/*" ); context.addEventListener( new ContextLoaderListener() );context.setInitParameter( "contextClass", AnnotationConfigWebApplicationContext.class.getName() );context.setInitParameter( "contextConfigLocation", AppConfig.class.getName() );// Add Spring Security Filter by the namecontext.addFilter(new FilterHolder( new DelegatingFilterProxy( "springSecurityFilterChain" ) ), "/*", EnumSet.allOf( DispatcherType.class ));server.setHandler( context );server.start();server.join(); } }除了過濾器部分,大多數(shù)代碼不需要任何說明。 這就是我所說的微妙的固有細節(jié): DelegatingFilterProxy應(yīng)該配置為過濾器名稱,該名稱必須完全是springSecurityFilterChain ,因為Spring Security會為其命名。 這樣,我們配置的安全規(guī)則將適用于任何JAX-RS服務(wù)調(diào)用(安全過濾器在Apache CXF servlet之前執(zhí)行),需要完全認證。 讓我們通過構(gòu)建和運行項目來快速檢查:
mvn clean package java -jar target/jax-rs-2.0-spring-security-0.0.1-SNAPSHOT.jar在不提供用戶名和密碼的情況下發(fā)出HTTP GET調(diào)用不會成功,并返回HTTP 狀態(tài)代碼401 。
> curl -i http://localhost:8080/rest/api/peopleHTTP/1.1 401 Full authentication is required to access this resource WWW-Authenticate: Basic realm="Realm" Cache-Control: must-revalidate,no-cache,no-store Content-Type: text/html; charset=ISO-8859-1 Content-Length: 339 Server: Jetty(9.2.2.v20140723)提供的用戶名和密碼相同的HTTP GET調(diào)用返回成功的響應(yīng)(服務(wù)器生成一些JSON)。
> curl -i -u user:password http://localhost:8080/rest/api/peopleHTTP/1.1 200 OK Date: Sun, 28 Sep 2014 20:07:35 GMT Content-Type: application/json Content-Length: 65 Server: Jetty(9.2.2.v20140723)[{"firstName":"Tom","lastName":"Tommyknocker","email":"a@b.com"}]太好了,它就像一個魅力! 事實證明,這確實非常容易。 同樣,如前所述,可以使用用戶詳細信息服務(wù)代替內(nèi)存中身份驗證,這是一個示例,該示例說明了如何進行:
package com.example.config;import java.util.Arrays;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException;@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true) public class UserDetailsSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService( userDetailsService() );}@Beanpublic UserDetailsService userDetailsService() {return new UserDetailsService() {@Overridepublic UserDetails loadUserByUsername( final String username ) throws UsernameNotFoundException {if( username.equals( "admin" ) ) {return new User( username, "password", true, true, true, true,Arrays.asList(new SimpleGrantedAuthority( "ROLE_USER" ),new SimpleGrantedAuthority( "ROLE_ADMIN" )));} else if ( username.equals( "user" ) ) {return new User( username, "password", true, true, true, true,Arrays.asList(new SimpleGrantedAuthority( "ROLE_USER" )));} return null;}};}@Overrideprotected void configure( HttpSecurity http ) throws Exception {http.httpBasic().and().sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ).and().authorizeRequests().antMatchers("/**").hasRole( "USER" );} }將AppConfig類中的@Import(InMemorySecurityConfig.class)替換為@Import( UserDetailsS??ecurityConfig.class)會得到相同的結(jié)果,因為兩個安全配置都定義了相同的用戶及其角色集。
我希望,此博客文章可以節(jié)省您一些時間,并為您提供一個良好的起點,因為Apache CXF和Spring Security在Jetty的保護下相處得很好!
- 完整的源代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2014/09/embedded-jetty-and-apache-cxf-secure-rest-services-with-spring-security.html
總結(jié)
以上是生活随笔為你收集整理的嵌入式Jetty和Apache CXF:借助Spring Security来保护REST服务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓光标(安卓 光标)
- 下一篇: cp在linux什么意思(linux