CAS: Invoking a webservice programmatically from within a servlet - java

I have two applications running on 2 different servers (one on tomcat and other on JBoss). Both these applications are connected to the same CAS server for authentication. Right now this CAS server also resides within the same JBoss.
Say:
App-1 --- is on tomcat and CASified
App-2 --- is on JBoss and CASified
CAS --- is on JBoss
Now that I am invoking an url of App-1 from a browser. The CAS login page comes up and after username/password is provided, the request now successfully enters the servlet of App-1. From this servlet code, I am trying to invoke a webservice that resides inside App-2.
Note: I use axis2 for this webservice and the axis2.war is also CASified
to the same CAS server for authentication.
I could not make this webservice call work no matter what I do. Is there a way to achieve this?
Note: If I call the CAS REST api with a hardcoded username/password, I am
getting the TGT, through which I am able to get the Service Ticket, with
which I am able to invoke that web-service. But I do not want to login again
with a hard-coded username or password. My webservice invocation should
happen with the already logged-in user only.

When you call the web service on App-2 are you getting a session cookie back? This should be the mechanism by which you're continuing to have access without reauthenticating on each call. If you're not getting a cookie back, then there's no way in which to continue access without authenticating each time (i.e. there's no way for the server to remember that it's you and that it should trust the rest of the message).

This is possible by using the CAS Proxy feature.
The link
https://wiki.jasig.org/display/CAS/Proxy+CAS+Walkthrough
helped a bit. But could not understand where to start with.
First take the CAS client jar from http://downloads.jasig.org/cas-clients/ . In my case I took cas-client-core-3.3.3.jar jar. I have included this jar in my application war.
In the web.xml of my application I have included the following 3 CAS Filters.
<!-- CAS Filters -->
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>https://cas-hostname.domainname:port/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>https://app-hostname.domainname:port</param-value>
</init-param>
<init-param>
<param-name>proxyCallbackUrl</param-name>
<param-value>https://app-hostname.domainname:port/app/ticket</param-value>
</init-param>
<init-param>
<param-name>proxyReceptorUrl</param-name>
<param-value>/app/ticket</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://cas-hostname.domainname:port/cas/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>https://app-hostname.domainname:port</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>
<!-- filter mappings -->
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/app/*</url-pattern>
<url-pattern>/ticket</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/app/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/app/*</url-pattern>
</filter-mapping>
Note-1: The order of filter mapping should be as mentioned above.
First CAS Validation filter mapping should come, followed by CAS
Authentication Filter and last the CAS HttpServletRequest Wrapper
filter.
Note-2: The URL pattern /ticket which is basically your proxy callback
url need not be mentioned in the last two filters.
Once CAS client jar is included in the web-app and web.xml configured with these filters, then all the http requests go through these filters.
So once your http requests entered your servlet, then you can call the following code snippet to get a proxy ticket:
String proxyTicket = ((AttributePrincipal) req.getUserPrincipal())
.getProxyTicketFor(webservice_url);
req is the HttpServletRequest Object and AttributePrincipal is a class which is present in the cas-client-core-3.3.3.jar
This proxyTicket can then be appended to your web-service's URL as a query string like the following:
https://myother-webservice-app.com/ws/myData?ticket=<proxyTicket>
Once this URL is constructed, then you can make the web-service call programmatically.
Hope this helps.

Related

How to do that service wriiten in java can handle requests only from the defined ip?

I want to write application in java that consists of few endpoints. I want that this endpoints handle requests only if They are sent from defined ip. How can I implement it?
Do something like this to get the IP address, that matches this address with your predefined IP addresses.
#RequestMapping(value = "startup", method = RequestMethod.GET)
public #ResponseBody ProcessResponse startUp(#RequestBody RequestTemplate requestTemplate, HttpServletRequest request) {
System.out.println(request.getRemoteAddr());
// some other code
}
Web Application
Apache Tomcat
Edit the WEB-INF/web.xml file in the application. Add the following:
<filter>
<filter-name>Remote Address Filter</filter-name>
<filter-class>org.apache.catalina.filters.RemoteAddrFilter</filter-class>
<init-param>
<param-name>allow</param-name>
<param-value><!-- insert your ip list / regex here --></param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Remote Address Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
This instructs the filter to block all IP addresses except the ones included. The filter supports blocking only the IP addresses listed. More information on the filter can be found here.
Spring Boot
For Spring Boot applications that run with the embedded Apache Tomcat container, which is the default behavior, use the same filter described in the Apache Tomcat section above. It is configured in the code instead of XML. The Spring Boot document explains how to enable a Servlet Filter. Use the instructions in the document to enable the RemoteAddrFilter.
Spring Boot Document : Document

CAS logout not working when deploying to different machines

If I use CAS server and my application in the same machine, logout is working fine for me. But if CAS server is deployed on the different machine and my application is deployed on the different machine, then cas logout is not working.
I am calling the following url from my application:
CAS Server URL: https://108.51.62.36:9443/cas/logout
And I have got the following entries in web.xml of my application:
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Included to support Single Logout. Note that the SingleSignOutFilter
is included in the springSecurityFilterChain. However, it could also be placed
as the first filter-mapping in the web.xml -->
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
Whe you use CAS, this create a secure cookie domain. You can see the value and modify in WEB-INF/spring-configuration:
ticketGrantingTicketCookieGenerator.xml
warnCookieGenerator.xml
So, I suggest don't use Ip. Use another like https://my.domaing.com:9443/cas/logout

Why does the basic AUTH box pop up twice in Chrome but not Firefox with spnego SSO

I have a pretty vanilla spnego SSO setup which is authenticating against an Active Directory server. The IE SSO contains the NEGOTIATE header cookie which is authenticating correctly without any prompting the user. Firefox and Chrome do not contain the SSO cookie, and hence fail back to the basic authentication. They do receive the username and password correctly and log in properly.
However, my minor annoyance is that it is prompting once in Firefox, but in Chrome it is prompting for the password twice.
Any ideas on why it could be prompting twice?
Setup below for completeness:
<filter>
<filter-name>SpnegoHttpFilter</filter-name>
<filter-class>net.sourceforge.spnego.SpnegoHttpFilter</filter-class>
<init-param>
<param-name>spnego.allow.basic</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>spnego.allow.localhost</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>spnego.allow.unsecure.basic</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>spnego.login.client.module</param-name>
<param-value>spnego-client</param-value>
</init-param>
<init-param>
<param-name>spnego.krb5.conf</param-name>
<param-value>xxxxxxxxxxxxx/krb5.conf</param-value>
</init-param>
<init-param>
<param-name>spnego.login.conf</param-name>
<param-value>xxxxxxxxxxxx/login.conf</param-value>
</init-param>
<init-param>
<param-name>spnego.preauth.username</param-name>
<param-value>xxxxxxxxxxxxxxxx</param-value>
</init-param>
<init-param>
<param-name>spnego.preauth.password</param-name>
<param-value>xxxxxxxxxxxxxxxx</param-value>
</init-param>
<init-param>
<param-name>spnego.login.server.module</param-name>
<param-value>spnego-server</param-value>
</init-param>
<init-param>
<param-name>spnego.prompt.ntlm</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>spnego.allow.delegation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
This isn't an explanation why it pops up twice but it is a way to debug it in chrome.
Browse to chrome:net-internals#events and you can see the 401 auth negotiation. The 401 requests and responses do not appear in the network tab of the chrome dev tools, so this is the only clue you can get.
Edit update - it seems that Chrome does not always send the Authorization headers for digest. Either because of pipelining, an auth cache bug, or heuristics for which urls "inherit" the authorization.
From https://groups.google.com/d/msg/chromium-discuss/9ASzOBdBrTQ/wUWFlwFYwaMJ
Since chromium will not pre-emptively send the authorization for
inferred protection space, it will keep entering that loop.
[...]
Support for Digest is likely, but a little sketchier, since the digest
auth model is broken under pipelining (next nonce is controlled by
previous server responses (Authorization-Info).
Because htdigest was causing two login dialogs every page load I switched to basic auth as my site was already using HTTPS for security. Basic and Digest do not define a scheme for which urls the Authorization token needs to be sent and for how long to cache the password or token. So it's slower and less secure than cookies. I'll try to avoid this scheme in the future.

Two separate Spring contexts for one webapp

I want to use two different Spring web contexts, each have own contextConfig, spring servlet and filter, that should be mapped to different urls. I have a
Standard Grails project, mapped to '/'
And an existing Spring webapp, that I want to map to /extra/
I know that I can deploy both into one Tomcat, but I'm looking for a way of making one app (one war, etc), because It can simplify our deployment and development process.
This applications don't need to share beans or anything, should be completely separate. Both have DispatcherServlet and DispatcherFilter (and both are using Spring Security, but different configuration)
How I can configure web.xml for such webapp?
I've tried to add new filter:
<filter>
<filter-name>extraSpringSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.extraSpring</param-value>
</init-param>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>extraSecurityFilterBean</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>extraSpringSecurityFilterChain</filter-name>
<url-pattern>/extra/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
and spring dispatcher servlet:
<servlet>
<servlet-name>extraSpring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>springConfigLocation</param-name>
<param-value>classpath:extra-spring-web.xml</param-value>
</init-param>
</servlet>
Where:
two context xml in classpath (inside exra library jar):
extra-spring-web.xml
extra-spring-security.xml (!!! how I should configure it?)
extra-spring-security.xml
is pretty standard Spring Security config
have configured bean extraSecurityFilterBean
have dependecy to beans from -web context (but it's not required to be)
It's semi-working now:
as I see from logs, extraSpring servlet successfully load beans from extra-spring-web.xml
but after accessing url /extra/ I got NoSuchBeanDefinitionException: No bean named 'extraSecurityFilterBean' is defined.
So, the question, how I can define context for DelegatingFilterProxy? I even tried to add this files into main context (contextConfigLocation param), it's not what i'm looking for, but it didn't work.
I've taken a look into DelegatingFilterProxy sources, but it's not clear for me how it loads the context.
As per my comment on the question, if the security filter chain is defined in extra-spring-security.xml then you need to ensure that that file is loaded by your extra DispatcherServlet in addition to extra-spring-web.xml either by <import>ing the -security file from the -web one or configuring it as:
<servlet>
<servlet-name>extraSpring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:extra-spring-web.xml
classpath:extra-spring-security.xml
</param-value>
</init-param>
</servlet>
You will also need to ensure that the security filter in the Grails application doesn't apply to /extra URIs, exactly how you do this depends on whether you're using annotations, database RequestMap entries etc.
If the modules are completely separate: the easiest way is to package them as two different webapp. Tens of different spring-based apps can run in one appserver -even on a modest developer machine- without issues.
A few questions
What does your Spring Security configuration look like?
I'm confused why the error states "No bean named 'apiservSecurityFilterChain' is defined" but the web.xml you have posted only references extraSpringSecurityFilterChain (the bean names should match or some important configuration is being left out).
Possible Answer
I'm guessing the problem is that the filter-name needs to match Spring Security's bean name (cannot know for sure without seeing the Spring Security configuration you are using). The default value used by the Spring Security namespace is springSecurityFilterChain, so try the following in the web.xml instead (notice extraSpringSecurityFilterChain changed to springSecurityFilterChain):
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.extraSpring</param-value>
</init-param>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>extraSecurityFilterBean</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/extra/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

How to create url mapping in web.xml

i want to create url pattern that lead to filter in jsf2.
I tried this code
<filter-mapping>
<filter-name>filter</filter-name>
<url-pattern>www.mysite.com</url-pattern>
</filter-mapping>
but i don't get to my filter.
can you help?
thanks
You are expecting to map the URL path, i.e. the part of URL that follows host and your web application name.
This is how full URL looks like:
http://www.mysite.com:8080/myapp/path1/path2/path3
where:
8080 is a port - optional - default 80
myapp - the context path of your web application. It is empty if your application is default web application on your app server.
path1/path2/path2 the path. This is what you are mapping using <filter-mapping> tag.
So, if for example you want to pass through your filter all requests to JSP pages say:
<url-pattern>*.jsp</url-pattern>
If your UI is under directory ui and you want to filter such requests say:
<url-pattern>/ui/*</url-pattern>
etc.
I hope I get this right:
You want to point an URL to your already created ServletFilter in your JSF 2.0 web application?
You have to register your filter in your webapps web.xml file and map it to your desired URL, e.g.
<!-- register your filter -->
<filter>
<filter-name>YourFilterName</filter-name>
<filter-class>com.your.filter.class</filter-class>
</filter>
<filter-mapping>
<filter-name>YourFilterName</filter-name>
<!-- Mapped to any URL -->
<url-pattern>/*</url-pattern>
</filter-mapping>

Categories