Java Jersey 2 implementing filters - java

I want to include a filter so i can check basic auth headers for each request
import java.io.IOException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.container.PreMatching;
#Provider
#PreMatching
public class CheckRequestFilter implements ClientRequestFilter {
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
ServiceProvider sp = new ServiceProvider();
/*if (sp.authenticateSP(requestContext.getHeaderString("authorization")) == false){ */
requestContext.abortWith(
Response.status(Response.Status.BAD_REQUEST)
.entity("User cannot be authenticated")
.build());
}}
//}
So far that's what i've come up with following the documentation here https://jersey.java.net/documentation/latest/user-guide.html#filters-and-interceptors
My problems is that i don't know how to include a filter so i can test it (i.e copy pasting the first example is not working) and if my approach is correct.
I want to limit it for certain requests in the future but for now i just want to to work.

You should use ContainerRequestFilter
Here is the API: https://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/container/ContainerRequestFilter.html
An here is an example:
public class AuthorizationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
final SecurityContext securityContext =
requestContext.getSecurityContext();
if (securityContext == null ||
!securityContext.isUserInRole("privileged")) {
requestContext.abortWith(Response
.status(Response.Status.UNAUTHORIZED)
.entity("User cannot access the resource.")
.build());
}
}
}

Related

Separate Grizzly HTTPHandlers for different verbs on same URL

I would like to handle a variety of HTTP methods on the same URL. i.e
GET http://localhost:8081/test
POST http://localhost:8081/test
I was originally thinking I could create a separate HTTPHandler for each method, but what I'm not clear on is how to route the request to the correct handler. Is there some filter higher up that I'm missing? I'm currently adding multiple HTTPHandlers to the server configuration, but only the first handler is being called.
Or do I have the wrong idea? Should I be using a single HTTPHandler for all requests to the same path and just check the HTTP method and respond accordingly?
import java.net.URI;
import javax.ws.rs.client.ClientBuilder;
import org.glassfish.grizzly.http.Method;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.grizzly.http.server.Response;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
public class HttpHandlerTest
{
private static final URI BASE_URI = URI.create("http://localhost:8081");
private static final String MAPPING = "/test";
public static void main(String[] args)
{
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, (GrizzlyHttpContainer) null, false, null, true);
server.getServerConfiguration().addHttpHandler(new GetHandler(), MAPPING);
server.getServerConfiguration().addHttpHandler(new PostHandler(), MAPPING);
javax.ws.rs.core.Response getResponse = ClientBuilder.newClient().target(BASE_URI + MAPPING).request().get();
System.out.println(getResponse.readEntity(String.class));
javax.ws.rs.core.Response postResponse = ClientBuilder.newClient().target(BASE_URI + MAPPING).request().post(null);
System.out.println(postResponse.readEntity(String.class));
server.shutdown();
}
public static class GetHandler extends HttpHandler{
#Override
public void service(Request request, Response response) throws Exception{
if (request.getMethod() == Method.GET){
// handle GET only, move on if method is something else
response.getWriter().write("GET response");
}
else
{
// let another HTTPHandler handle this
response.getWriter().write("I wanted a different handler to handle this");
}
}
}
public static class PostHandler extends HttpHandler{
#Override
public void service(Request request, Response response) throws Exception{
if (request.getMethod() == Method.POST){
// handle GET only, move on if method is something else
response.getWriter().write("POST response");
}
}
}
}

Is there a way to hook into every call to a #Controller method or every call to the Spring MVC dispatcher servlet? [duplicate]

I use Spring MVC (4.0.1) as a backend for rest services and angularjs as frontend.
every request to my server backend has a http-header with a session id
I can read this header in my server backend with the following code:
#Autowired
protected HttpServletRequest request;
String xHeader=request.getHeader("X-Auth-Token"); //returns the sessionID from the header
Now I call this method getPermission(xHeader) it return only true or false. If the user exists in my DB it return true else false!
I want now create a filter with this behavior, that checks every request if the user have the permission to access my controllers! But if the method returns false it should send back a 401 error and not reach my controller!
How can I do this and create my own filter? I use only Java Config and no XML.
I think I must add the filter here:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
MyOwnFilter=new MyOwnFilter();
return new Filter[] {MyOwnFilter};
}
}
Alternative to Filters, you can use HandlerInterceptor.
public class SessionManager implements HandlerInterceptor{
// This method is called before the controller
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String xHeader = request.getHeader("X-Auth-Token");
boolean permission = getPermission(xHeader);
if(permission) {
return true;
}
else {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
// Above code will send a 401 with no response body.
// If you need a 401 view, do a redirect instead of
// returning false.
// response.sendRedirect("/401"); // assuming you have a handler mapping for 401
}
return false;
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
And then add this interceptor to your webmvc config.
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
SessionManager getSessionManager() {
return new SessionManager();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getSessionManager())
.addPathPatterns("/**")
.excludePathPatterns("/resources/**", "/login");
// assuming you put your serve your static files with /resources/ mapping
// and the pre login page is served with /login mapping
}
}
Below is the filter to perform the logic you have mentioned
#WebFilter("/*")
public class AuthTokenFilter implements Filter {
#Override
public void destroy() {
// ...
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String xHeader = ((HttpServletRequest)request).getHeader("X-Auth-Token");
if(getPermission(xHeader)) {
chain.doFilter(request, response);
} else {
request.getRequestDispatcher("401.html").forward(request, response);
}
}
}
And you got it right, the spring config should be following.
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new AuthTokenFilter()};
}
}
Spring can use filters, but they recommend that you use their version of filters, known as an interceptor
http://viralpatel.net/blogs/spring-mvc-interceptor-example/
There is a quick run through of how they work. They are nearly identical to filters, but designed to work inside the Spring MVC lifecycle.
I assume that you are trying to implement some kind of OAuth security which is based on jwt token.
Nowdays there are several ways to do so but here is my favourite one:
Here is how the filter looks like:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.filter.GenericFilterBean;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
public class JwtFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest req,
final ServletResponse res,
final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new ServletException("Missing or invalid Authorization header.");
}
final String token = authHeader.substring(7); // The part after "Bearer "
try {
final Claims claims = Jwts.parser().setSigningKey("secretkey")
.parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
}
catch (final SignatureException e) {
throw new ServletException("Invalid token.");
}
chain.doFilter(req, res);
}
}
Pretty simple there is the user controller also where you can find the login method:
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#RestController
#RequestMapping("/user")
public class UserController {
private final Map<String, List<String>> userDb = new HashMap<>();
public UserController() {
userDb.put("tom", Arrays.asList("user"));
userDb.put("sally", Arrays.asList("user", "admin"));
}
#RequestMapping(value = "login", method = RequestMethod.POST)
public LoginResponse login(#RequestBody final UserLogin login)
throws ServletException {
if (login.name == null || !userDb.containsKey(login.name)) {
throw new ServletException("Invalid login");
}
return new LoginResponse(Jwts.builder().setSubject(login.name)
.claim("roles", userDb.get(login.name)).setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "secretkey").compact());
}
#SuppressWarnings("unused")
private static class UserLogin {
public String name;
public String password;
}
#SuppressWarnings("unused")
private static class LoginResponse {
public String token;
public LoginResponse(final String token) {
this.token = token;
}
}
}
Of course we have Main where you can see the filter bean:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#EnableAutoConfiguration
#ComponentScan
#Configuration
public class WebApplication {
#Bean
public FilterRegistrationBean jwtFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
public static void main(final String[] args) throws Exception {
SpringApplication.run(WebApplication.class, args);
}
}
Last but not least there is an example controller:
import io.jsonwebtoken.Claims;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api")
public class ApiController {
#SuppressWarnings("unchecked")
#RequestMapping(value = "role/{role}", method = RequestMethod.GET)
public Boolean login(#PathVariable final String role,
final HttpServletRequest request) throws ServletException {
final Claims claims = (Claims) request.getAttribute("claims");
return ((List<String>) claims.get("roles")).contains(role);
}
}
Here is a link to GitHub all thanks goes to nielsutrecht for the great work I have used this project as base and it works perfectly.
You can also implement it using an aspect with a pointcut that targets a certain annotation. I have written a library that enables you to use annotations that perform authorization checks based on a JWT token.
You can find the project with all the documentation on: https://github.com/nille85/jwt-aspect. I have used this approach multiple times in order to secure a REST Backend that is consumed by a single page application.
I have also documented on my blog how you can use it in a Spring MVC Application: http://www.nille.be/security/creating-authorization-server-using-jwts/
The following is an extract from the example project on https://github.com/nille85/auth-server
The example underneath contains a protected method getClient. The annotation #Authorize that the aspect uses checks if the value from the "aud jwt claim" matches the clientId parameter that is annotated with #ClaimValue. If it matches, the method can be entered. Otherwise an exception is thrown.
#RestController
#RequestMapping(path = "/clients")
public class ClientController {
private final ClientService clientService;
#Autowired
public ClientController(final ClientService clientService) {
this.clientService = clientService;
}
#Authorize("hasClaim('aud','#clientid')")
#RequestMapping(value = "/{clientid}", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Client getClient(#PathVariable(value = "clientid") #ClaimValue(value = "clientid") final String clientId) {
return clientService.getClient(clientId);
}
#RequestMapping(value = "", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody List<Client> getClients() {
return clientService.getClients();
}
#RequestMapping(path = "", method = RequestMethod.POST, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Client registerClient(#RequestBody RegisterClientCommand command) {
return clientService.register(command);
}
}
The Aspect itself can be configured like:
#Bean
public JWTAspect jwtAspect() {
JWTAspect aspect = new JWTAspect(payloadService());
return aspect;
}
The PayloadService that is needed can for example be implemented like:
public class PayloadRequestService implements PayloadService {
private final JWTVerifier verifier;
public PayloadRequestService(final JWTVerifier verifier){
this.verifier = verifier;
}
#Override
public Payload verify() {
ServletRequestAttributes t = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = t.getRequest();
final String jwtValue = request.getHeader("X-AUTH");
JWT jwt = new JWT(jwtValue);
Payload payload =verifier.verify(jwt);
return payload;
}
}
You can create and configure your own filter by doing following steps.
1) Create your class by implementing the filter interface and override its methods.
public class MyFilter implements javax.servlet.Filter{
public void destroy(){}
public void doFilter(Request, Response, FilterChain){//do what you want to filter
}
........
}
2) Now configure your filter in web.xml
<filter>
<filter-name>myFilter</filter-name>
<filter-class>MyFilter</filter-class>
</filter>
3) Now provide url mapping of the filter.
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
4) Now restart your server and check all the web request will first come to MyFilter and then proceed to the respective controller.
Hopefully it will be the required answer.
Your approach looks correct.
Once I have used something similar to following (Removed most of the lines and kept it simple).
public class MvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ERROR);
FilterRegistration.Dynamic monitoringFilter = servletContext.addFilter("monitoringFilter", MonitoringFilter.class);
monitoringFilter.addMappingForUrlPatterns(dispatcherTypes, false, "/api/admin/*");
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Also you need a custom filter looks like below.
public class CustomXHeaderFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String xHeader = request.getHeader("X-Auth-Token");
if(YOUR xHeader validation fails){
//Redirect to a view
//OR something similar
return;
}else{
//If the xHeader is OK, go through the chain as a proper request
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
Hope this helps.
Additionally you can use FilterRegistrationBean if you Spring Boot. It does the same thing (I think so) which FilterRegistration.Dynamic does.

CORS issue with JSON data

I'm getting the below CORS issue in my Angular 7 application when running locally.
Access to XMLHttpRequest at 'http://localhost:8080/OnlineOrganicMarket/api/changeorderstatus' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I'm using Jersey RESTful Web Services framework at the backend. This happens only with header type application/json. It is working for application/x-www-form-urlencoded.
I think there is a problem with my CORSFilter class.
package com.oom.services.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.server.ExtendedUriInfo;
#Provider
#Priority(Priorities.HEADER_DECORATOR)
public class CORSFilter implements ContainerRequestFilter, ContainerResponseFilter {
private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
private static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
private static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
private static final String AUTHORIZATION = "authorization";
private static final String ORIGIN = "Origin";
private static String extractAllowedMethods(final ExtendedUriInfo extendedUriInfo) {
final Optional<Class<?>> optional = extendedUriInfo.getMatchedRuntimeResources().stream()
.flatMap(r -> r.getResources().stream()).flatMap(r -> r.getHandlerClasses().stream())
.filter(r -> r.getPackage().getName().startsWith("com.oom.services")).findFirst();
if (optional.isPresent()) {
return Arrays.stream(optional.get().getDeclaredMethods())//
.flatMap(m -> Arrays.stream(m.getAnnotations()))//
.map(a -> a.annotationType().getAnnotation(javax.ws.rs.HttpMethod.class))//
.filter(Objects::nonNull)//
.map(HttpMethod::value)//
.distinct()//
.collect(Collectors.joining(", "));
}
// Returning OPTIONS is a bit shady, as ACAM is about *real*, actual methods only.
return "OPTIONS";
}
#Context
private HttpServletRequest request;
#Context
private ExtendedUriInfo extendedUriInfo;
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
final String origin = requestContext.getHeaderString(ORIGIN);
if (origin != null && "OPTIONS".equals(requestContext.getMethod())) {
request.setAttribute(this.getClass().getName(), true);
requestContext.abortWith(Response.ok("CORS OK, carry on.", MediaType.TEXT_PLAIN_TYPE).build());
}
}
/**
* #see https://www.w3.org/TR/cors/
* #see https://jmchung.github.io/blog/2013/08/11/cross-domain-on-jersey-restful-web-services/
* #see https://solutionsisee.wordpress.com/2016/06/30/adding-cors-support-in-jersey-server/
*/
#Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
throws IOException {
final MultivaluedMap<String, Object> responseHeaders = responseContext.getHeaders();
final String origin = requestContext.getHeaderString(ORIGIN);
if (origin != null) {
// The presence of the Origin header marks a CORS request.
responseHeaders.add(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
responseHeaders.add(ACCESS_CONTROL_ALLOW_METHODS, extractAllowedMethods(extendedUriInfo));
responseHeaders.add(ACCESS_CONTROL_ALLOW_HEADERS, AUTHORIZATION + ", X-Requested-With, Content-Type");
if (requestContext.getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS) != null) {
responseHeaders.add(ACCESS_CONTROL_ALLOW_CREDENTIALS, requestContext
.getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS).toLowerCase().contains(AUTHORIZATION));
}
}
if (request.getAttribute(this.getClass().getName()) != null) {
// We are in a CORS Preflight answer, fast tracked. The entity (== response body) is not
// relevant.
}
}
}
My service part where I'm having issue
#POST
#Path("/changeorderstatus")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response updateOrderStatus(OrderBean orderStatus) {
JSONArray responseJson = new OrderDAO().updateOrderStatus(orderStatus);
return Response.ok(responseJson, MediaType.APPLICATION_JSON).header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS").build();
}
I tried disabling CORS through some extensions and disabling it in the chrome browser. Nothing worked.
As commented by #mamounothman, I tried out the below code from Paul Samsotha's answer
It started working when I added Content-type key in the Access-Control-Allow-Headers
Originally answered by Paul Samsotha
package com.oom.services.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.server.ExtendedUriInfo;
#Provider
#PreMatching
public class CORSFilter implements ContainerRequestFilter, ContainerResponseFilter {
/**
* Method for ContainerRequestFilter.
*/
#Override
public void filter(ContainerRequestContext request) throws IOException {
// If it's a preflight request, we abort the request with
// a 200 status, and the CORS headers are added in the
// response filter method below.
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
/**
* A preflight request is an OPTIONS request
* with an Origin header.
*/
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
/**
* Method for ContainerResponseFilter.
*/
#Override
public void filter(ContainerRequestContext request, ContainerResponseContext response)
throws IOException {
// if there is no Origin header, then it is not a
// cross origin request. We don't do anything.
if (request.getHeaderString("Origin") == null) {
return;
}
// If it is a preflight request, then we add all
// the CORS headers here.
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don't want.
"X-Requested-With, Authorization, " +
"Accept-Version, Content-type, Content-MD5, CSRF-Token");
}
// Cross origin requests can be either simple requests
// or preflight request. We need to add this header
// to both type of requests. Only preflight requests
// need the previously added headers.
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
Still I'm not getting why the previous code I was using did not work with the header type Content-Type - application/json even though it had the header type Content-Type in the Access-Control-Allow-Header's list.

How to configure waffle in spring using java configuration

I have been struggling to get waffle to work with spring 4.2.5 using spring java configuration. And I thought I might as well help others in the same situation.
We use a custom preWaffle and postWaffle filter to authenticate that the user exists in our database after it has been validated via waffles NTLM protocol.
We also have methods for authorization of a user actions using the EnableGlobalMethodSecurity annotation.
To get this working in spring java configuration was trouble some to say the least. You can find our solution in the answer below. I hope it will help.
SpringConfiguration.java
// ... imports
#Configuration
#EnableWebMvc
#EnableScheduling
#PropertySources({
#PropertySource("classpath:app.properties")
// ... Properties sources
})
#EnableTransactionManagement
#ComponentScan(basePackages = "com.our.package")
public class SpringConfiguration extends WebMvcConfigurerAdapter {
// Our Spring configuration ...
}
SecurityConfiguration.java
// ... imports
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
#Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
// Authentication manager configuration
#Autowired
private WindowsAuthenticationProviderWrapper authProvider;
#Autowired
private AuthenticationManagerBuilder auth;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Bean
public AuthenticationManager authenticationManager() throws Exception {
return auth.getObject();
}
// Waffle configuration
#Bean
public Filter customPreAuthSecurityFilter() {
return new CustomPreAuthSecurityFilter();
}
#Bean
public Filter customNegotiateSecurityFilter() {
return new CustomNegotiateSecurityFilter();
}
#Bean
public WindowsAuthProviderImpl waffleAuthProvider(){
return new WindowsAuthProviderImpl();
}
#Bean(name="negotiateSecurityFilterProvider")
#Autowired
public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(){
NegotiateSecurityFilterProvider bean = new NegotiateSecurityFilterProvider(waffleAuthProvider());
List<String> protocols = new ArrayList<>();
protocols.add("Negotiate");
bean.setProtocols(protocols);
return bean;
}
#Bean
public BasicSecurityFilterProvider basicSecurityFilterProvider(){
return new BasicSecurityFilterProvider(waffleAuthProvider());
}
#Bean(name="waffleSecurityFilterProviderCollection")
#Autowired
public waffle.servlet.spi.SecurityFilterProviderCollection negotiateSecurityFilterProviderCollection() {
final List<SecurityFilterProvider> lsp = new ArrayList<>();
lsp.add(negotiateSecurityFilterProvider());
lsp.add(basicSecurityFilterProvider());
return new waffle.servlet.spi.SecurityFilterProviderCollection(lsp.toArray(new SecurityFilterProvider[]{}));
}
#Bean(name="negotiateSecurityFilterEntryPoint")
#Autowired
public waffle.spring.NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint() {
final waffle.spring.NegotiateSecurityFilterEntryPoint ep = new waffle.spring.NegotiateSecurityFilterEntryPoint();
ep.setProvider(negotiateSecurityFilterProviderCollection());
return ep;
}
#Bean(name="negotiateSecurityFilter")
#Autowired
public waffle.spring.NegotiateSecurityFilter waffleNegotiateSecurityFilter(){
waffle.spring.NegotiateSecurityFilter bean = new waffle.spring.NegotiateSecurityFilter();
bean.setRoleFormat("both");
bean.setPrincipalFormat("fqn");
bean.setAllowGuestLogin(false);
bean.setProvider(negotiateSecurityFilterProviderCollection());
return bean;
}
// Static Mappings
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
}
// Security filter chain
// The custom filters can be removed if you only use waffle
// but this is how we added them
#Override
protected void configure(HttpSecurity http) throws Exception {
// A user needs to have the role user and has to be authenticated
http.exceptionHandling()
.authenticationEntryPoint(negotiateSecurityFilterEntryPoint()).and()
.addFilterBefore(customPreAuthSecurityFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(waffleNegotiateSecurityFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(customNegotiateSecurityFilter(), BasicAuthenticationFilter.class)
.authorizeRequests().anyRequest().fullyAuthenticated();
}
}
To be able to autowire the waffle authProvider I created the following wrapperclass.
WindowsAuthenticationProviderWrapper.java
// ... imports
// This class purpose is only to make the Windows authentication provider autowireable in spring.
#Component
public class WindowsAuthenticationProviderWrapper extends WindowsAuthenticationProvider{}
As requested (Some code has been stripped due to security risks).
CustomPreAuthFilter.java
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* This filter removes the excess negoatiate header sent by IE. If the client
* has already authenticated, strip the Authorization header from the request.
*/
public class CustomPreAuthSecurityFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
SecurityContext sec = SecurityContextHolder.getContext();
HttpServletRequest req = (HttpServletRequest) servletRequest;
if(sec != null && sec.getAuthentication() != null) {
req = new CustomServletRequestWrapper(req);
}
try {
filterChain.doFilter(req, servletResponse);
} catch (RuntimeException e) {
sendUnauthorized((HttpServletResponse) servletResponse);
}
}
private void sendUnauthorized(HttpServletResponse response) throws IOException {
logger.warn("error logging in user");
SecurityContextHolder.clearContext();
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
CustomNegotiateSecurityFilter.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import waffle.servlet.WindowsPrincipal;
import waffle.spring.WindowsAuthenticationToken;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/**
* Handle post NTLM authentication against system database
*/
public class CustomNegotiateSecurityFilter extends GenericFilterBean {
#Autowired
private UserDAO userDAO;
#Autowired
Environment env;
private static final Logger LOGGER = LoggerFactory.getLogger(CustomNegotiateSecurityFilter.class);
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
SecurityContext sec = SecurityContextHolder.getContext();
Authentication authentication = sec.getAuthentication();
// Continue filter chain if we are anonymously authenticated or if DB authentication has already happened.
if (authentication != null && authentication.getClass() == WindowsAuthenticationToken.class) {
// The user is Authenticated with NTLM but needs to be checked against the DB.
User user;
try {
// fetch user from DB ...
} catch (Exception e) {
// The could not be found in the DB.
sendUnauthorized(response);
return;
}
// The user was found in the DB.
WindowsPrincipal principal = (WindowsPrincipal)authentication.getPrincipal();
final CustomAuthenticationToken token = new CustomAuthenticationToken(principal); // This class extends WindowsAuthenticationToken
// add roles to token ...
sec.setAuthentication(token);
}
chain.doFilter(request, response);
}
private void sendUnauthorized(HttpServletResponse response) throws IOException {
logger.warn("Could not log in user");
SecurityContextHolder.clearContext();
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
private void addRoleToAuthentication(WindowsAuthenticationToken authentication, String role) {
for(GrantedAuthority authority : authentication.getAuthorities()) {
if(authority.getAuthority().equals(role)) {
return;
}
}
authentication.getAuthorities().add(new SimpleGrantedAuthority(role));
}
}
EDIT
For those who asked about here is one implementation. CustomServletRequestWrapper:
class CustomServletRequestWrapper extends HttpServletRequestWrapper {
public CustomServletRequestWrapper(HttpServletRequest request) {
super(request);
}
public String getHeader(String name) {
if(name.equals("Authorization"))
return null;
String header = super.getHeader(name);
return (header != null) ? header : super.getParameter(name); // Note: you can't use getParameterValues() here.
}
public Enumeration getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
names.addAll(Collections.list(super.getParameterNames()));
names.remove("Authorization");
return Collections.enumeration(names);
}
}
If you need more information don't hessitate to ask.

How to create my own filter with Spring MVC?

I use Spring MVC (4.0.1) as a backend for rest services and angularjs as frontend.
every request to my server backend has a http-header with a session id
I can read this header in my server backend with the following code:
#Autowired
protected HttpServletRequest request;
String xHeader=request.getHeader("X-Auth-Token"); //returns the sessionID from the header
Now I call this method getPermission(xHeader) it return only true or false. If the user exists in my DB it return true else false!
I want now create a filter with this behavior, that checks every request if the user have the permission to access my controllers! But if the method returns false it should send back a 401 error and not reach my controller!
How can I do this and create my own filter? I use only Java Config and no XML.
I think I must add the filter here:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
MyOwnFilter=new MyOwnFilter();
return new Filter[] {MyOwnFilter};
}
}
Alternative to Filters, you can use HandlerInterceptor.
public class SessionManager implements HandlerInterceptor{
// This method is called before the controller
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String xHeader = request.getHeader("X-Auth-Token");
boolean permission = getPermission(xHeader);
if(permission) {
return true;
}
else {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
// Above code will send a 401 with no response body.
// If you need a 401 view, do a redirect instead of
// returning false.
// response.sendRedirect("/401"); // assuming you have a handler mapping for 401
}
return false;
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
And then add this interceptor to your webmvc config.
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
SessionManager getSessionManager() {
return new SessionManager();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getSessionManager())
.addPathPatterns("/**")
.excludePathPatterns("/resources/**", "/login");
// assuming you put your serve your static files with /resources/ mapping
// and the pre login page is served with /login mapping
}
}
Below is the filter to perform the logic you have mentioned
#WebFilter("/*")
public class AuthTokenFilter implements Filter {
#Override
public void destroy() {
// ...
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String xHeader = ((HttpServletRequest)request).getHeader("X-Auth-Token");
if(getPermission(xHeader)) {
chain.doFilter(request, response);
} else {
request.getRequestDispatcher("401.html").forward(request, response);
}
}
}
And you got it right, the spring config should be following.
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new AuthTokenFilter()};
}
}
Spring can use filters, but they recommend that you use their version of filters, known as an interceptor
http://viralpatel.net/blogs/spring-mvc-interceptor-example/
There is a quick run through of how they work. They are nearly identical to filters, but designed to work inside the Spring MVC lifecycle.
I assume that you are trying to implement some kind of OAuth security which is based on jwt token.
Nowdays there are several ways to do so but here is my favourite one:
Here is how the filter looks like:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.filter.GenericFilterBean;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
public class JwtFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest req,
final ServletResponse res,
final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new ServletException("Missing or invalid Authorization header.");
}
final String token = authHeader.substring(7); // The part after "Bearer "
try {
final Claims claims = Jwts.parser().setSigningKey("secretkey")
.parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
}
catch (final SignatureException e) {
throw new ServletException("Invalid token.");
}
chain.doFilter(req, res);
}
}
Pretty simple there is the user controller also where you can find the login method:
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#RestController
#RequestMapping("/user")
public class UserController {
private final Map<String, List<String>> userDb = new HashMap<>();
public UserController() {
userDb.put("tom", Arrays.asList("user"));
userDb.put("sally", Arrays.asList("user", "admin"));
}
#RequestMapping(value = "login", method = RequestMethod.POST)
public LoginResponse login(#RequestBody final UserLogin login)
throws ServletException {
if (login.name == null || !userDb.containsKey(login.name)) {
throw new ServletException("Invalid login");
}
return new LoginResponse(Jwts.builder().setSubject(login.name)
.claim("roles", userDb.get(login.name)).setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "secretkey").compact());
}
#SuppressWarnings("unused")
private static class UserLogin {
public String name;
public String password;
}
#SuppressWarnings("unused")
private static class LoginResponse {
public String token;
public LoginResponse(final String token) {
this.token = token;
}
}
}
Of course we have Main where you can see the filter bean:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#EnableAutoConfiguration
#ComponentScan
#Configuration
public class WebApplication {
#Bean
public FilterRegistrationBean jwtFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
public static void main(final String[] args) throws Exception {
SpringApplication.run(WebApplication.class, args);
}
}
Last but not least there is an example controller:
import io.jsonwebtoken.Claims;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api")
public class ApiController {
#SuppressWarnings("unchecked")
#RequestMapping(value = "role/{role}", method = RequestMethod.GET)
public Boolean login(#PathVariable final String role,
final HttpServletRequest request) throws ServletException {
final Claims claims = (Claims) request.getAttribute("claims");
return ((List<String>) claims.get("roles")).contains(role);
}
}
Here is a link to GitHub all thanks goes to nielsutrecht for the great work I have used this project as base and it works perfectly.
You can also implement it using an aspect with a pointcut that targets a certain annotation. I have written a library that enables you to use annotations that perform authorization checks based on a JWT token.
You can find the project with all the documentation on: https://github.com/nille85/jwt-aspect. I have used this approach multiple times in order to secure a REST Backend that is consumed by a single page application.
I have also documented on my blog how you can use it in a Spring MVC Application: http://www.nille.be/security/creating-authorization-server-using-jwts/
The following is an extract from the example project on https://github.com/nille85/auth-server
The example underneath contains a protected method getClient. The annotation #Authorize that the aspect uses checks if the value from the "aud jwt claim" matches the clientId parameter that is annotated with #ClaimValue. If it matches, the method can be entered. Otherwise an exception is thrown.
#RestController
#RequestMapping(path = "/clients")
public class ClientController {
private final ClientService clientService;
#Autowired
public ClientController(final ClientService clientService) {
this.clientService = clientService;
}
#Authorize("hasClaim('aud','#clientid')")
#RequestMapping(value = "/{clientid}", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Client getClient(#PathVariable(value = "clientid") #ClaimValue(value = "clientid") final String clientId) {
return clientService.getClient(clientId);
}
#RequestMapping(value = "", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody List<Client> getClients() {
return clientService.getClients();
}
#RequestMapping(path = "", method = RequestMethod.POST, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Client registerClient(#RequestBody RegisterClientCommand command) {
return clientService.register(command);
}
}
The Aspect itself can be configured like:
#Bean
public JWTAspect jwtAspect() {
JWTAspect aspect = new JWTAspect(payloadService());
return aspect;
}
The PayloadService that is needed can for example be implemented like:
public class PayloadRequestService implements PayloadService {
private final JWTVerifier verifier;
public PayloadRequestService(final JWTVerifier verifier){
this.verifier = verifier;
}
#Override
public Payload verify() {
ServletRequestAttributes t = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = t.getRequest();
final String jwtValue = request.getHeader("X-AUTH");
JWT jwt = new JWT(jwtValue);
Payload payload =verifier.verify(jwt);
return payload;
}
}
You can create and configure your own filter by doing following steps.
1) Create your class by implementing the filter interface and override its methods.
public class MyFilter implements javax.servlet.Filter{
public void destroy(){}
public void doFilter(Request, Response, FilterChain){//do what you want to filter
}
........
}
2) Now configure your filter in web.xml
<filter>
<filter-name>myFilter</filter-name>
<filter-class>MyFilter</filter-class>
</filter>
3) Now provide url mapping of the filter.
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
4) Now restart your server and check all the web request will first come to MyFilter and then proceed to the respective controller.
Hopefully it will be the required answer.
Your approach looks correct.
Once I have used something similar to following (Removed most of the lines and kept it simple).
public class MvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ERROR);
FilterRegistration.Dynamic monitoringFilter = servletContext.addFilter("monitoringFilter", MonitoringFilter.class);
monitoringFilter.addMappingForUrlPatterns(dispatcherTypes, false, "/api/admin/*");
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Also you need a custom filter looks like below.
public class CustomXHeaderFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String xHeader = request.getHeader("X-Auth-Token");
if(YOUR xHeader validation fails){
//Redirect to a view
//OR something similar
return;
}else{
//If the xHeader is OK, go through the chain as a proper request
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
Hope this helps.
Additionally you can use FilterRegistrationBean if you Spring Boot. It does the same thing (I think so) which FilterRegistration.Dynamic does.

Categories