结合 Spring 安全 AUTHORIZATION 承载和 CXF
Combine Spring security AUTHORIZATION bearer and CXF
我正在为我的 restful 使用 Spring 安全 + Spring 核心并结合 CXF。
配置如下:
web.xml 对于 CXF 配置:
<!-- Spring configuration for ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- CXF configuration for resful webservices -->
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
配置 CXF 端点(context.xml)
<!-- configure for restful endpoint for application services as web authentication... -->
<jaxrs:server id="ApplicationServices"
address="/Application">
<jaxrs:serviceBeans>
<ref bean="ControllerImpl" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="jsonProvider" />
</jaxrs:providers>
<jaxrs:features>
<bean id="loggingFeature"
class="org.apache.cxf.feature.LoggingFeature">
<property name="prettyLogging" value="true" />
</bean>
<ref bean="swagger2Feature" />
</jaxrs:features>
</jaxrs:server>
spring 安全配置 - 过滤器
public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
AuthenticationFilter(final RequestMatcher requiresAuth) {
super(requiresAuth);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
//Optional<String> tokenParam = Optional.ofNullable(httpServletRequest.getHeader(AUTHORIZATION)); //Authorization: Bearer TOKEN
String token= StringUtils.isNotEmpty(httpServletRequest.getHeader(AUTHORIZATION))? httpServletRequest.getHeader(AUTHORIZATION) : "";
token= StringUtils.removeStart(token, "Bearer").trim();
Authentication requestAuthentication = new UsernamePasswordAuthenticationToken(token, token);
return getAuthenticationManager().authenticate(requestAuthentication);
}
@Override
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
spring 安全配置 - 提供商
@Component
public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Autowired
UserTokenService userTokenService;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
//
}
@Override
protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
Object token= usernamePasswordAuthenticationToken.getCredentials();
return Optional
.ofNullable(token)
.map(String::valueOf)
.flatMap(userTokenService::findByToken)
.orElseThrow(() -> new UsernameNotFoundException("Cannot find user with authentication token=" + token));
}
}
spring 安全配置 - SecurityConfiguration
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/services/**"));
AuthenticationProvider provider;
public SecurityConfiguration(final AuthenticationProvider authenticationProvider) {
super();
this.provider = authenticationProvider;
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(provider);
}
/**
* we don't need provide this service for now because we are using Vaadin
*/
@Override
public void configure(final WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/token/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().exceptionHandling().and()
.authenticationProvider(provider)
.addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class).authorizeRequests()
.requestMatchers(PROTECTED_URLS).authenticated().and().csrf().disable().formLogin().disable()
.httpBasic().disable().logout().disable();
}
@Bean
AuthenticationFilter authenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
// filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
@Bean
AuthenticationEntryPoint forbiddenEntryPoint() {
return new HttpStatusEntryPoint(HttpStatus.FORBIDDEN);
}
}
findByToken
@Override
public Optional<User> findByToken(String token) {
UserToken userToken = userTokenDAO.findByToken(token);
if (userToken != null) {
User user = new User(userToken.getUserId(), userToken.getUserPassword(), true, true, true, true,
AuthorityUtils.createAuthorityList("USER"));
return Optional.of(user);
}
return Optional.empty();
}
但是过滤器不起作用。该请求仍然允许在没有任何 spring 安全验证的情况下进行。
这样的请求:
curl -X POST "http://localhost:8080/my-app/services/Application/ControllerImpl/myservice1" -H "accept: application/json" -H "Content-Type: application/json" -d "string"
没有异常或错误。上面请求returns200(OK)。我预计会失败,因为请求中没有不记名令牌。
我们如何结合 Spring 安全性(使用不记名令牌方法)和 CXF?
Based on discussion in the comments, it is clear spring security filter chain is not getting configured.
您能否将以下内容也添加到您的 web.xml 中,看看执行是否命中 AntPathRequestMatcher
matches
方法
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
我正在为我的 restful 使用 Spring 安全 + Spring 核心并结合 CXF。
配置如下:
web.xml 对于 CXF 配置:
<!-- Spring configuration for ContextLoaderListener --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- CXF configuration for resful webservices --> <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping>
配置 CXF 端点(context.xml)
<!-- configure for restful endpoint for application services as web authentication... --> <jaxrs:server id="ApplicationServices" address="/Application"> <jaxrs:serviceBeans> <ref bean="ControllerImpl" /> </jaxrs:serviceBeans> <jaxrs:providers> <ref bean="jsonProvider" /> </jaxrs:providers> <jaxrs:features> <bean id="loggingFeature" class="org.apache.cxf.feature.LoggingFeature"> <property name="prettyLogging" value="true" /> </bean> <ref bean="swagger2Feature" /> </jaxrs:features> </jaxrs:server>
spring 安全配置 - 过滤器
public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter { AuthenticationFilter(final RequestMatcher requiresAuth) { super(requiresAuth); } @Override public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException { //Optional<String> tokenParam = Optional.ofNullable(httpServletRequest.getHeader(AUTHORIZATION)); //Authorization: Bearer TOKEN String token= StringUtils.isNotEmpty(httpServletRequest.getHeader(AUTHORIZATION))? httpServletRequest.getHeader(AUTHORIZATION) : ""; token= StringUtils.removeStart(token, "Bearer").trim(); Authentication requestAuthentication = new UsernamePasswordAuthenticationToken(token, token); return getAuthenticationManager().authenticate(requestAuthentication); } @Override protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authResult) throws IOException, ServletException { SecurityContextHolder.getContext().setAuthentication(authResult); chain.doFilter(request, response); } }
spring 安全配置 - 提供商
@Component public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { @Autowired UserTokenService userTokenService; @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException { // } @Override protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException { Object token= usernamePasswordAuthenticationToken.getCredentials(); return Optional .ofNullable(token) .map(String::valueOf) .flatMap(userTokenService::findByToken) .orElseThrow(() -> new UsernameNotFoundException("Cannot find user with authentication token=" + token)); } }
spring 安全配置 - SecurityConfiguration
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher( new AntPathRequestMatcher("/services/**")); AuthenticationProvider provider; public SecurityConfiguration(final AuthenticationProvider authenticationProvider) { super(); this.provider = authenticationProvider; } @Override protected void configure(final AuthenticationManagerBuilder auth) { auth.authenticationProvider(provider); } /** * we don't need provide this service for now because we are using Vaadin */ @Override public void configure(final WebSecurity webSecurity) { webSecurity.ignoring().antMatchers("/token/**"); } @Override public void configure(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().exceptionHandling().and() .authenticationProvider(provider) .addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class).authorizeRequests() .requestMatchers(PROTECTED_URLS).authenticated().and().csrf().disable().formLogin().disable() .httpBasic().disable().logout().disable(); } @Bean AuthenticationFilter authenticationFilter() throws Exception { final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS); filter.setAuthenticationManager(authenticationManager()); // filter.setAuthenticationSuccessHandler(successHandler()); return filter; } @Bean AuthenticationEntryPoint forbiddenEntryPoint() { return new HttpStatusEntryPoint(HttpStatus.FORBIDDEN); } }
findByToken
@Override public Optional<User> findByToken(String token) { UserToken userToken = userTokenDAO.findByToken(token); if (userToken != null) { User user = new User(userToken.getUserId(), userToken.getUserPassword(), true, true, true, true, AuthorityUtils.createAuthorityList("USER")); return Optional.of(user); } return Optional.empty(); }
但是过滤器不起作用。该请求仍然允许在没有任何 spring 安全验证的情况下进行。
这样的请求:
curl -X POST "http://localhost:8080/my-app/services/Application/ControllerImpl/myservice1" -H "accept: application/json" -H "Content-Type: application/json" -d "string"
没有异常或错误。上面请求returns200(OK)。我预计会失败,因为请求中没有不记名令牌。
我们如何结合 Spring 安全性(使用不记名令牌方法)和 CXF?
Based on discussion in the comments, it is clear spring security filter chain is not getting configured.
您能否将以下内容也添加到您的 web.xml 中,看看执行是否命中 AntPathRequestMatcher
matches
方法
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>