java - Wildfly web.xml security constraint blocking basic auth header for JAX-RS methods using ContainerRequestFilter -


the web application i'm developing consists of servlets , jax-rs webservices. until now, using containerrequestfilter authenticate rest method calls need secure servlets decided use web.xml define security constraints. web.xml looks this:

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">     <security-constraint>         <web-resource-collection>             <web-resource-name>rest</web-resource-name>             <url-pattern>/rest/*</url-pattern>         </web-resource-collection>     </security-constraint>     <security-constraint>         <web-resource-collection>             <web-resource-name>protected</web-resource-name>             <url-pattern>/protected/*</url-pattern>         </web-resource-collection>         <auth-constraint>             <role-name>admin</role-name>         </auth-constraint>     </security-constraint>     <security-role>         <role-name>admin</role-name>     </security-role>     <security-role>         <role-name>user</role-name>     </security-role>     <!-- configure login http basic -->     <login-config>         <auth-method>basic</auth-method>         <realm-name>restricted zone</realm-name>     </login-config> </web-app> 

if understand syntax of web.xml correctly, defined means access /rest/* (where jax-rs methods are) unrestricted far loginmodules concerned, , access /protected/* path (where keep secure servlets) requires basic authorization.

when try open 1 of secure servlets, e.g. /protected/test, basic auth login dialog in browser , behavior correct - if enter credentials 'admin' user, i'm allowed access. otherwise, 'forbidden' message.

also, when try access on /rest/ path no basic auth dialog, expect. however, authorization header in containerrequestfilter not 1 i'm sending in rest request it's 1 used /protected/ servlet.

below other parts of puzzle:

standalone.xml (security-domains section)

<security-domain name="palosecuritydomain" cache-type="default">     <authentication>         <login-module code="com.palo.security.palologinmodule" flag="required"/>     </authentication> </security-domain> 

jboss-web.xml

<?xml version="1.0" encoding="utf-8"?> <jboss-web>     <security-domain>palosecuritydomain</security-domain> </jboss-web> 

palologinmodule.java

package com.palo.security;  import java.security.acl.group; import java.util.set;  import javax.inject.inject; import javax.naming.namingexception; import javax.security.auth.login.loginexception;  import org.apache.log4j.logger; import org.jboss.security.simplegroup; import org.jboss.security.simpleprincipal; import org.jboss.security.auth.spi.usernamepasswordloginmodule;  import com.palo.palorealmrole; import com.palo.model.palorealmuser; import com.palo.utils.cdihelper; import com.palo.utils.passwordhandler;  public class palorealmloginmodule extends usernamepasswordloginmodule {    private static logger logger = logger       .getlogger(palorealmloginmodule.class);    @inject   private palorealmlogic realmlogic;    @override   protected string getuserspassword() throws loginexception {     if (null == realmlogic) {       try {         cdihelper.programmaticinjection(palorealmloginmodule.class,             this);       } catch (namingexception e) {         // todo auto-generated catch block         e.printstacktrace();       }     }     logger.debug("getting password user " + super.getusername());     palorealmuser user = realmlogic.getuserbyname(super.getusername());     if (null == user) {       logger.error("user not found");       throw new loginexception("user " + super.getusername()           + " not found");     }     logger.debug("found " + user.getpassword());     return user.getpassword();   }    @override   protected group[] getrolesets() throws loginexception {     logger.debug("getting roles user " + super.getusername());     if (null == realmlogic) {       try {         cdihelper.programmaticinjection(palorealmloginmodule.class,             this);       } catch (namingexception e) {         // todo auto-generated catch block         e.printstacktrace();       }     }     palorealmuser user = realmlogic.getuserbyname(super.getusername());     if (null == user) {       throw new loginexception("user " + super.getusername()           + " not found");     }     set<palorealmrole> roles = user.getroles();     group[] groups = { new simplegroup("roles") };     (palorealmrole role : roles) {       logger.debug("found role " + role.getrole());       simpleprincipal prole = new simpleprincipal(role.getrole());       groups[0].addmember(prole);     }      return groups;   }    @override   protected boolean validatepassword(string inputpassword,       string expectedpassword) {     logger.debug("validating password " + inputpassword + "|"         + expectedpassword);     return passwordhandler.getinstance().verifypassword(inputpassword,         expectedpassword);   }  } 

securityinterceptor.java

package com.palo.web.rest;  import java.io.ioexception; import java.lang.reflect.method; import java.util.list; import java.util.stringtokenizer;  import javax.annotation.security.denyall; import javax.annotation.security.permitall; import javax.inject.inject; import javax.json.jsonobjectbuilder; 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.multivaluedmap; import javax.ws.rs.core.response; import javax.ws.rs.ext.provider;  import org.apache.log4j.logger; import org.jboss.resteasy.annotations.interception.serverinterceptor; import org.jboss.resteasy.core.headers; import org.jboss.resteasy.core.resourcemethodinvoker; import org.jboss.resteasy.core.serverresponse;  import com.palo.analytics.googleanalyticsevent; import com.palo.logic.userlogic; import com.palo.web.utils.httputils;  @provider @serverinterceptor public class securityinterceptor implements containerrequestfilter {    private static logger logger = logger.getlogger(securityinterceptor.class);    private static final string authorization_property = "authorization";   private static final serverresponse access_denied = new serverresponse(       "access denied resource", 401, new headers<object>());   private static final serverresponse access_denied_for_user = new serverresponse(       "user not authorized", 401, new headers<object>());   private static final serverresponse access_forbidden = new serverresponse(       "nobody can access resource", 403, new headers<object>());    @inject   private userlogic ul;    @override   /**    * request filter called automatically called each incoming request. checks method being called client and, based on method's annotations, restricts access, verifies identity of caller, checks validity of session token, etc.    */   public void filter(containerrequestcontext requestcontext)       throws ioexception {     logger.debug("------------- request filter ------------");     resourcemethodinvoker methodinvoker = (resourcemethodinvoker) requestcontext         .getproperty("org.jboss.resteasy.core.resourcemethodinvoker");     method method = methodinvoker.getmethod();     string methodname = method.getname();     string uri = requestcontext.geturiinfo().getpath();      logger.debug("accessing method " + methodname + " via uri " + uri);      (string str : requestcontext.getpropertynames()) {       logger.debug(str);     }      // request headers     final multivaluedmap<string, string> headers = requestcontext         .getheaders();     (string key : headers.keyset()) {       (string value : headers.get(key)) {         logger.debug(key + " - " + value);       }     }      // access allowed     if (method.isannotationpresent(permitall.class)) {       return;     }     // access denied     if (method.isannotationpresent(denyall.class)) {       requestcontext.abortwith(access_forbidden);       return;     }      // fetch authorization header     final list<string> authorization = headers.get(authorization_property);      // if no authorization information present; block access     if (null == authorization || authorization.isempty()) {       requestcontext.abortwith(access_denied);       return;     }      final string username = httputils.getusernamefromauthorizationheader(         authorization, httputils.authentication_scheme_basic);     final string password = httputils.getpasswordfromauthenticationheader(         authorization, httputils.authentication_scheme_basic);      if (null == username || null == password || username.isempty()         || password.isempty()) {       requestcontext.abortwith(access_denied_for_user);       return;     }      boolean authenticated = ul.authenticate(username, password);     if (false == authenticated) {       requestcontext.abortwith(access_denied);       return;     }      return;   } } 

i'm using restclient firefox send rest requests jax-rs methods. since i'm logging headers, can plainly see comes filter , value doesn't change between calls, if change in restclient. more, value still there if don't use authorization header in restclient.

my question why authorization header blocked , it's not forwarded filter? if remove web.xml file, correct authorization header in containerrequestfilter. there way move /rest part of application zone not affected login-config in web.xml?

any appreciated!

from understand, if specify login-config, it's used resources, specified in web-resource-collection. both /rest/ , /protected/ in case.

first approach 1 thing do, modify login module, assigns admin role users have provided valid credentials, , assigns anonymous role those, has not provided valid credentials. modify web.xml this

    <security-constraint>         <web-resource-collection>             <web-resource-name>rest</web-resource-name>             <url-pattern>/rest/*</url-pattern>         </web-resource-collection>         <auth-constraint>             <role-name>anonymous</role-name>             <role-name>admin</role-name>         </auth-constraint>     </security-constraint>     <security-constraint>         <web-resource-collection>             <web-resource-name>protected</web-resource-name>             <url-pattern>/protected/*</url-pattern>         </web-resource-collection>         <auth-constraint>             <role-name>admin</role-name>         </auth-constraint>     </security-constraint> 


second approach instead of modifying login module, adding 1 more login module security domain, assign anonymous role

third approach use custom authentication mechanism http://undertow.io/documentation/core/security.html basic authentication mechanism expects user send credentials in http header in format authorization: basic: base64encodedcredentials

when using custom authentication mechanism, have access request path, , make custom authentication mechanism skip call login modules in case request made path don't want protected. don't think approach, these kinds of decisions should made login modules+web.xml.


fourth approach (not sure if works, does resources, not specified in security-constraints, aren't checked login modules. so, make /rest/ resource unprotected, remove these lines web.xml:

<security-constraint>         <web-resource-collection>             <web-resource-name>rest</web-resource-name>             <url-pattern>/rest/*</url-pattern>         </web-resource-collection>     </security-constraint> 

Comments

Popular posts from this blog

javascript - Jquery show_hide, what to add in order to make the page scroll to the bottom of the hidden field once button is clicked -

javascript - Highcharts multi-color line -

javascript - Enter key does not work in search box -