I would like to chain a base64 encoder and gzip in jax-rs via interceptors.
The encoder is implemented as follows:
#EncryptPayload
#Provider
#Priority(Priorities.ENTITY_CODER)
public class EncryptPayloadInterceptor implements WriterInterceptor {
private Logger logger;
#Inject
public EncryptPayloadInterceptor(Logger logger) {
this.logger = logger;
}
public EncryptPayloadInterceptor() {
super();
}
#Override
public void aroundWriteTo(WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException {
logger.error("Calling EncryptPayload Interceptor: ");
final OutputStream outputStream = writerInterceptorContext.getOutputStream();
writerInterceptorContext.getHeaders().putSingle("X-ENCRYPTED", "true");
writerInterceptorContext.setOutputStream(new Base64OutputStream((outputStream)));
writerInterceptorContext.proceed();
}
}
The gzip part is implemented as follows:
#Provider
#Priority(Priorities.USER)
public class GZIPWriterInterceptor implements WriterInterceptor {
private Logger logger;
#Inject
public GZIPWriterInterceptor(Logger logger) {
this.logger = logger;
}
public GZIPWriterInterceptor() {
super();
}
#Override
public void aroundWriteTo(WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException {
logger.error("Calling GZIPWriter Interceptor: ");
final OutputStream outputStream = writerInterceptorContext.getOutputStream();
writerInterceptorContext.setOutputStream(new GZIPOutputStream(outputStream));
writerInterceptorContext.getHeaders().putSingle("Content-Encoding", "gzip");
writerInterceptorContext.proceed();
}
}
When disabling gzip as a provider it works nicely. When enabling the provider GZIP I receive the following error within jersey:
LOGGER.log(Level.SEVERE, LocalizationMessages.ERROR_COMMITTING_OUTPUT_STREAM(), e);
First the encoding is called and after that the gzip is called. It seems that Base64 might close the stream.
Any help highly appreciated.
Related
We were using Resteasy 2 but we are upgrading to Resteasy 3 and the HttpServletRequest injection is always null.
Our modified security interceptor/filter that looks like:
#Provider
#ServerInterceptor
#Precedence("SECURITY")
public class SecurityInterceptor implements ContainerRequestFilter, ContainerResponseFilter {
#Context
private HttpServletRequest servletRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Need access to "servletRequest" but it is always null
if (!isTokenValid(pmContext, method)) {
requestContext.abortWith(ACCESS_DENIED);
}
}
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
// post processing
}
}
And the application class looks like:
#ApplicationPath("/")
public class RestApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> empty = new HashSet<Class<?>>();
public RestApplication() {
// Interceptors
this.singletons.add(new SecurityInterceptor());
// Services
this.singletons.add(new MyService());
}
public Set<Class<?>> getClasses() {
return this.empty;
}
public Set<Object> getSingletons() {
return this.singletons;
}
}
Sample API:
#Path("/test")
public class MyService extends BaseService {
#Context HttpServletRequest servletRequest;
#GET
#Path("/hello")
#Produces(MediaType.APPLICATION_JSON)
public Response hello() {
// Need access to HttpServletRequest but it's null
return Response.ok("hello").build();
}
}
However, looking at this and this posts, I don't see HttpServletRequest injection provider.
This leads me to believe that I may need an additional plugin. This is what is installed:
jose-jwt
resteasy-atom-provider
resteasy-cdi
resteasy-crypto
resteasy-jackson2-provider
resteasy-jackson-provider
resteasy-jaxb-provider
resteasy-jaxrs
resteasy-jettison-provider
resteasy-jsapi
resteasy-json-p-provider
resteasy-multipart-provider
resteasy-spring
resteasy-validator-provider-11
resteasy-yaml-provider
Any ideas?
Based on #peeskillet suggestion, modifying to return new class instances instead of singletons resolved my issue.
Thus my modified javax.ws.rs.core.Application file looks like:
#ApplicationPath("/")
public class RestApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> classes = new HashSet<Class<?>>();
public RestApplication() {
// Interceptors
this.classes.add(SecurityInterceptor.class);
// Services
this.classes.add(MyService.class);
}
public Set<Class<?>> getClasses() {
return this.classes;
}
public Set<Object> getSingletons() {
return this.singletons;
}
}
You could use the SecurityInterceptor's constructor to get these values:
///...
private HttpServletRequest request;
private ServletContext context;
public SecurityInterceptor(#Context HttpServletRequest request, #Context ServletContext context) {
this.request = request;
this.context = context;
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// The "servletRequest" won't be null anymore
if (!isTokenValid(pmContext, method)) {
requestContext.abortWith(ACCESS_DENIED);
}
}
///...
This'll solve your problem
I am working with Spring 4 REST API annotation based configuration application. I want to add response header on each/every request once user is authenticate by JWT. I have created interceptor for that which looks as below:
public class AuthenticateInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3)
throws Exception {
response.addHeader("afterCompletion", "afterCompletion header");
response.setHeader("afterCompletion", "afterCompletion header");
System.out.println("************** afterCompletion **************");
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object,
ModelAndView modelAndView) throws Exception {
response.addHeader("postHandle", "postHandle header");
System.out.println("************** postHandle **************");
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
System.out.println("************** preHandle **************");
return true;
}
}
My interceptor configuration is as below:
#Configuration
public class AdapterConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthenticateInterceptor());
}
}
When I get JSON response I am not able the see the added header value which are added from interceptor. Any one help me what is the issue and how can I add header from interceptor for each/every request.
I didn't succeed in interceptors, Instead using Filters or WebFilter perfectly works:
#Component
public class ResponseHeaderWebFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("CustomHeaderName", "SomeValue");
chain.doFilter(request, response);
}
}
In case you are using webflux reactive component, then :
#Component
public class ResponseHeaderWebFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
exchange.getResponse()
.getHeaders()
.add("CustomHeaderName", "SomeValue");
return chain.filter(exchange);
}
}
If it can help you, I've managed it like this: https://stackoverflow.com/a/49431665/4939245
Look at second point
You can put the response header for each call in the application (this is for Spring annotation-based):
#Component
public class Filter extends OncePerRequestFilter {
....
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//response.addHeader("Access-Control-Allow-Origin", "*");
//response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Cache-Control", "no-store"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setHeader("Expires", "0"); // Proxies.
filterChain.doFilter(request, response);
}
}
I hope I was helpful!
You are almost doing same but would like to give you consitant way of achieving same. Please do following changes.
1) Add #Component annotation on AuthenticateInterceptor class. And you package containing this class should be in packages scanned list.
#Component
public class AuthenticateInterceptor implements HandlerInterceptor {
...
}
2) Autowire and inject instance of AuthenticateInterceptor like below.
#Configuration
public class AdapterConfig extends WebMvcConfigurerAdapter {
#Autowired
private AuthenticateInterceptor authenticateInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticateInterceptor);
}
}
I have Spring Boot application, everything works fine until I implement spring security in front of my application. This is a RESTful api that has a token based authentication. What's even more weird it works (!) intermittently - by intermittently I mean restarting the application will return the right responses such as 401/403 if unauthenticated and other codes if user is authorized to access them. This is being deployed into WebLogic.
2017-01-05 14:12:51.164 WARN 11252 --- [ (self-tuning)'] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/user] in DispatcherServlet with name 'dispatcherServlet'
WebApplication.java
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class WebApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
Object[] sources = new Object[2];
sources[0] = WebConfiguration.class;
sources[1] = WebSecurityConfiguration.class;
SpringApplication.run(sources, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebApplication.class);
}
}
WebConfiguration.java
#Configuration
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao"})
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class })
public class WebConfiguration extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(WebConfiguration.class);
/**
* Setup a simple strategy: use all the defaults and return XML by default
* when not sure.
*/
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON).mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
#Bean(name = "entityManagerFactory")
public EntityManagerFactory getQmsEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName(Config.PERSISTENCE_UNIT_NAME);
em.setPersistenceXmlLocation("META-INF/persistence.xml");
em.setDataSource(getDataSource());
em.setJpaVendorAdapter(getJpaHibernateVendorAdapter());
em.afterPropertiesSet();
return em.getObject();
}
#Bean
public HibernateJpaVendorAdapter getJpaHibernateVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(true);
// adapter.setDatabase("ORACLE");
adapter.setDatabasePlatform("org.hibernate.dialect.Oracle10gDialect");
return adapter;
}
#Bean(name="dataSource", destroyMethod = "")
//http://stackoverflow.com/questions/19158837/weblogic-datasource-disappears-from-jndi-tree
#Qualifier("dataSource")
#Profile("weblogic")
public DataSource dataSource() {
DataSource dataSource = null;
JndiTemplate jndi = new JndiTemplate();
try {
dataSource = (DataSource) jndi.lookup("jdbc/datasource");
} catch (NamingException e) {
logger.error("NamingException for jdbc/datasource", e);
}
return dataSource;
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}
};
}
}
WebSecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan({
"com.subject",
"com.custom"
})
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private StatelessAuthenticationFilter statelessAuthenticationFilter;
#Autowired
private RestAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private CusAuthenticationProvider cusAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(cusAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.securityContext()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilterBefore(statelessAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler);
}
}
StatelessAuthenticationFilter.java
#Component
public class StatelessAuthenticationFilter extends OncePerRequestFilter {
#Inject
private SubjectLookupService subjectLookupService;
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authenticateUser(request));
filterChain.doFilter(request, response);
}
private Authentication authenticateUser(HttpServletRequest request) {
try {
String application = StringUtils.defaultString(request.getParameter("application"));
UserInfo me = subjectLookupService.getUserInfo();
List<GrantedAuthority> roles = me.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())).collect(Collectors.toList());
UserDetails user = new User(me.getUsername(), "", roles);
Authentication authentication = new UserAuthentication(user);
return authentication;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Controller.java
#RestController
public class Controller {
#Autowired
private QService qService;
#PreAuthorize("hasAnyRole('view', 'admin')")
#RequestMapping(value = "/q/{year}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> listQuotas(#PathVariable Integer year) {
return new ResponseEntity<>(qService.listQs(year), HttpStatus.OK);
}
#RequestMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> user(HttpServletRequest request) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return new ResponseEntity<>( auth.getPrincipal(), HttpStatus.OK);
}
#PreAuthorize("hasRole('shouldntauthorize')")
#RequestMapping(value = "/unauthorized/{year}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> unauthorized(#PathVariable Integer year) {
return new ResponseEntity<>(qService.listQs(year), HttpStatus.OK);
}
}
When it works - I am able to hit any of the above methods using HTTP gets and I am getting correct responses. When it's not working, I am constantly getting:
2017-01-05 14:18:47.506 WARN 11252 --- [ (self-tuning)'] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/user] in DispatcherServlet with name 'dispatcherServlet'
I can verify in the logs that when Spring Boot initializes the application is also sets the correct mapping URL.
Any ideas what could be the problem here?
when you say "intermittently" I tend to think that the problem is with Spring startup configuration.
So, I'd be weary on the fact that you have #ComponentScan twice, and with different packages.
Could you try removing
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao"})
from class WebConfiguration.java and
#ComponentScan({ "com.subject", "com.custom" })
from class WebSecurityConfiguration.java, and replace them with a single
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao", "com.subject", "com.custom"})
in the main SpringBoot class?
Could anyone please help me with an example of a Spring Boot application that contains a Rest Service with endpoints protected by Spring Security using oAuth2 with user credentials from a MySQL database?
How about this one: https://github.com/spring-projects/spring-security-oauth/tree/master/tests/annotation/jdbc (it's not MySQL, but it's JDBC, so the transformation is trivial)?
Please refer to https://github.com/royclarkson/spring-rest-service-oauth/
and perform following changes, It uses primary datasource defined in application.properties,
#Configuration
public class OAuth2ServerConfiguration {
private static final String RESOURCE_ID = "rest_api";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/users").hasRole("ADMIN")
.antMatchers("/review").authenticated()
.antMatchers("/logreview").authenticated()
.antMatchers("/oauth/token").authenticated()
.and()
.csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
;
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
DataSource dataSource;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(new JdbcTokenStore(dataSource))
.authenticationManager(this.authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.jdbc(dataSource);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setAccessTokenValiditySeconds(300);
tokenServices.setRefreshTokenValiditySeconds(6000);
tokenServices.setTokenStore(new JdbcTokenStore(dataSource));
return tokenServices;
}
}
}
Log output of my RemoteServiceServlet (GWT) is not shown in Logfiles or Stdout when using getServletContext().log("anything");
For dependency injection I use Google Guice. For my own log output I use slf4j-jdk14. I tried this in Tomcat 6 as well as in Jetty (GWT devmode).
To make it clear, my Servlet:
#Singleton
public class MyServiceServlet extends RemoteServiceServlet implements MyService {
private static final Logger log = LoggerFactory.getLogger(MyServiceServlet.class);
private final ADependency dep;
#Inject
public MyServiceServlet(ADependency dep) {
getServletContext().log("THIS IS NOT SHOWN IN MY LOGS");
log.error("THIS IS SHOWN IN MY LOGS");
this.dep = dep;
}
}
So, where can I find the missing log output or where can I configure the ServletContext-Log?
The ServletContext.log method behavior is container specific. The method I have used to make it consistent is to wrap the ServletConfig passed in through init() in order to create a wrapped ServletContext which uses our own provided logger (Slf4j in this case).
public class Slf4jServletConfigWrapper implements ServletConfig {
private final ServletConfig config;
private final Logger log;
public Slf4jServletConfigWrapper(Logger log, ServletConfig config) {
this.log = log;
this.config = config;
}
public ServletContext getServletContext() {
return new ServletContext() {
public void log(String message, Throwable throwable) {
log.info(message, throwable);
}
public void log(Exception exception, String msg) {
log.info(msg, exception);
}
public void log(String msg) {
log.info(msg);
}
...
Full Slf4jServletConfigWrapper.java code
In your Servlet override the init() method to use the ServletConfig wrapper
public void init(final ServletConfig config) throws ServletException {
super.init(new Slf4jServletConfigWrapper(log, config));
}