I have a Spring MVC REST service running locally through Tomcat on port 8080. When I hit the service directly through the browser or fiddler, it works as expected. I also have a local website built with AngularJS. If I place this website in the Tomcat directory and navigate to the website's link served by Tomcat, everything still works fine.
The problem is when I don't place the website in the Tomcat directory. Say I place the website project on my desktop and navigate to a page that is supposed to retrieve data from the local REST service... I can see in fiddler that the request is going through, hitting the service, and the service returns the data... but the website doesn't display it!
Why would it work fine only when both are served by Tomcat?
Link to hit service directly:
http://localhost:8080/CPProject/users (returns all the users, in JSON format)
Website (this works):
http://localhost:8080/CPWebsite/app/#/users
Website (doesn't work):
http://localhost:63342/CPWebsite/app/index.html#/users (63342 is the port used by Webstorm 7.0)
or
file:///C:/Users/someuser/CPWebsite/app/index.html#/users
My Angular controllers look like this:
cpControllers.controller('UserListCtrl',
function($scope, $http) {
$http.get('http://localhost:8080/CP/users')
.success(function(data) {
$scope.users = data;
}
)
}
);
I've also created a CORS filter in my Sprint MVC service:
#Component
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS, HEAD");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
You'll need to add correct configured CORS filter to your app that allows preflight requests. Here's an example configuration:
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.allowGenericHttpRequests</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.allowOrigin</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowSubdomains</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, HEAD, POST, PUT, DELETE, OPTIONS</param-value>
</init-param>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.supportsCredentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.maxAge</param-name>
<param-value>3600</param-value>
</init-param>
<init-param>
<param-name>cors.tagRequests</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>
Thanks for the answers above. It turns out my CORS filter was fine, but I had forgotten to add it to my application initializer in order for the filter to actually work:
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
WebApplicationContext rootContext = createRootContext(servletContext);
configureSpringMvc(servletContext, rootContext);
FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", SimpleCORSFilter.class);
corsFilter.addMappingForUrlPatterns(null, false, "/*");
}
See here for more info: http://jpgmr.wordpress.com/2013/12/12/cross-origin-resource-sharing-cors-requests-with-spring-mvc/
Related
i am having issues securing my .html files with shiro.
my app is .html/jquery with rest calls.
user hit landing page redirects to /secure/index.html
user logs in.
user redirected to secure page /secure/index.html
user logouts and goes to /logout.html (shiro confirms logout)
type in browser address "/secure/index.html" and the page is
displayed despite being logged out.
at step5. the access,log is not showing any activity.
according to chrome developer tools the page is loading from cache.
the issue seems to be the browser is not reloading the page and hence shiro filter never gets to fire, and the contents of the secure html file is visible.
it is very evident that the browser cache bypasses the shiro auth.
only if i Ctl+F5 refresh will the page be reload from jetty and security is activated.
shiro.ini
======================
authc.loginUrl = /login.html
authc.usernameParam = username
authc.passwordParam = password
authc.rememberMeParam = remember
logout.redirectUrl = /logout.html
Solution #1
my solution was to force the browser to have a 'no-cache'on the .html files.
however i want cache control for my static content.
i know the DefaultServlet can do this so i created another 2 instances of the DefaultServlet (staticDefaultServlet and webDefaultServlet) to server different cache-control headers for my content.
<servlet>
<servlet-name>webDefaultServlet</servlet-name>
<servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
<init-param>
<param-name>cacheControl</param-name>
<param-value>max-age=5,no-store</param-value>
</init-param>
<init-param>
<param-name>pathInfoOnly</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>staticDefaultServlet</servlet-name>
<servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
<init-param>
<param-name>cacheControl</param-name>
<param-value>max-age=604800</param-value>
</init-param>
<init-param>
<param-name>pathInfoOnly</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>staticDefaultServlet</servlet-name>
<url-pattern>/static/**</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>webDefaultServlet</servlet-name>
<url-pattern>/pages/**</url-pattern>
</servlet-mapping>
however the cache headers are not coming thru.
Questions
Q1. can i use multiple instances of DefaultServlet to have different each control for different paths?
Q2. Is there another mechanism to protect .html files with shiro?
thanks
-lp
this is too difficult.
i abandoned the defaultservlet solution and instead used a Filter to manage my cacheControl for different paths.
public class CacheControlFilter implements Filter
{
String cacheControl;
//#Override
public void init(FilterConfig filterConfig) throws ServletException
{
cacheControl = filterConfig.getInitParameter("cacheControl");
}
//#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
if (response instanceof HttpServletResponse)
{
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.addHeader("Cache-Control", cacheControl);
}
chain.doFilter(request, response);
}
and deployed in web.xml
<filter>
<filter-name>WebCacheControlFilter</filter-name>
<filter-class>com.asteriski.itaxreport.filter.CacheControlFilter</filter-class>
<init-param>
<param-name>cacheControl</param-name>
<param-value>no-cache, no-store, must-revalidate</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>WebCacheControlFilter</filter-name>
<url-pattern>/pages/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
-lp
I need to establish a connexion between my android client and my spring + JSF server
I need to send files from my android client and receive them in the server side
for that, i tried to send the file directly to a bean which contains this function
#Component
#Scope("request")
#Path("/file")
public class RestWebService {
#POST
#Path("/upload")
#Consumes(javax.ws.rs.core.MediaType.TEXT_PLAIN)
public String uploadFile(
#FormParam("file") InputStream uploadedInputStream,
#FormParam("file") FormDataContentDisposition fileDetail,
#QueryParam("inputFileName") String inputFileName) throws FileNotFoundException{
System.out.println("start upload");
System.out.println(fileDetail.getFileName());
return "";
}
and i added to my web.xml this lines:
<display-name>Restful Web Application</display-name>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet- class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.test.WebService</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
but it seems that JSF block the connexion
i want to know if it is possible to continue in this way or i must change this solution and in this case how ??
JSF not blocking REST access url. You need just properly configuration.
You can have a look JSF/REST project which is run under wildfly, you can access REST via url within JSF application, please have a look
https://github.com/armdev/wildfly-jsf2.3-web
I want to load a web context not by xml, but configure programatically.
If I use the method setwar(), it worked.
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app id = "DERBYNET_ID">
<servlet>
<servlet-name>H2Console</servlet-name>
<servlet-class>org.h2.server.web.WebServlet</servlet-class>
<init-param>
<param-name>webAllowOthers</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>H2Console</servlet-name>
<url-pattern>/console/*</url-pattern>
</servlet-mapping>
</web-app>
But, I want to remove webapp. becasuse it has a xml only. So, I want to set the servlet programmatically like below.But, it's not working. I can't see the error log on the console when I run application. But, If I connect the web page, it gives 503 status code. Why this source code doesn't work??
ServletHandler servletHandler = new ServletHandler();
ServletHolder holder = new ServletHolder("H2Console", (Class<? extends Servlet>) WebServlet.class);
holder.setInitOrder(1);
holder.setInitParameter("webAllowOthers", "true");
ServletHolder[] holders = { holder };
ServletMapping servletMapping = new ServletMapping();
servletMapping.setServletName("H2Console");
servletMapping.setPathSpec("/console/*");
ServletMapping[] servletMappings = {servletMapping};
servletHandler.setServlets(holders);
servletHandler.setServletMappings(servletMappings);
derbyWebapp.setServletHandler(servletHandler);
My web.xml is
<servlet>
<servlet-name>SwipeXBackendServices</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.mycompany.backend.webservices</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SwipeXBackendServices</servlet-name>
<url-pattern>/webservices/*</url-pattern>
</servlet-mapping>
Why does the mapping have to be /webservices/* and not com.mycompany.backend.webservices/*.
This is my webservice code
package com.swipex.backend.webservices;
#Path("/Activation")
public class Activation {
private static final Logger log = Logger.getLogger(Activation.class);
private DeviceDetails deviceDetailsTable = DeviceDetails.getInstance();
#POST
#Path("Request")
#Consumes({ MediaType.APPLICATION_JSON })
public Response post(JAXBElement<CDeviceDetails> device) {
Edit : Junit Client Code
#Test
public void testPost() throws Exception {
/*
* Activate
*/
// Initialize Web Services
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
//
// Activation Service
//
URI url = UriBuilder.fromUri(
"http://" + SERVER + "/SwipeXBackEnd/Activation/Request")
.build();
WebResource service = client.resource(url);
System.out.println(url);
// Get the data ready
CDeviceDetails newDevice = new CDeviceDetails(null, "mac id", "model",
"manufacturer", "androidVersion", Calendar.getInstance()
.getTime(), "buildID", "fingerPrint", "board",
"bootLoader", "hardware", "id", "product", "serial", Calendar
.getInstance().getTime(), Calendar.getInstance()
.getTime(), Globals.ACTIVE);
String deviceUniqueIdentity = service.type(MediaType.APPLICATION_JSON)
.post(String.class, newDevice);
assertNotNull(deviceUniqueIdentity);
System.out.println("Activation Passed " + deviceUniqueIdentity);
Url pattern doesn't have any relationship with your java package structure. In your case, you've mapped the SwipeXBackendServices servlet to listen on the '/webservices' url (ex : http://localhost:8080/webservices/). i.e., when a request comes for '/webservices', it'll be routed to your 'SwipeXBackendServices' servlet class.
And also your jax-rs (rest) class 'Activation' is mapped to the url '/activation' (ex : http://localhost:8080/activation).
Mapping of url & class :
/webservices ===> SwipeXBackendServices
/activation ===> Activation
So, don't confuse the package name with the url name.
As Sriram says the servlet mappings have no relation with the packages.
The com.sun.jersey.config.property.packages parameter for the SwipeXBackendServices servlet just tells Jersey which package to scan for discovering REST resources.
The servlet-mapping for the SwipeXBackendServices servlet tells the servlet container on which URLs the servlet will answer. In this case, it will answer to all URLs prefixed with /webservices. Meaning that all REST resources will be prefixed with /webservices.
Then, the URLs for any REST resource will be the concatenation of
The WAR context root
The Jersey servlet mapping
The REST resource path (being itself the concatenation of the class's path and the method's path)
Hence, assuming the context root for your WAR is /myWebApp, the URL for a POST request to your Activation resource will be: http://localhost:8080/myWebApp/webservices/Activation/Request
Desperately need help here securing a simple Apache CXF web service. Attempts with Spring Security is taking me no where so I need to find a different strategy. This is to implement authorization on a legacy Java service implemented for some of our clients.
This simple Apache CXF web service was created using Maven's cxf-jaxws-javafirst prototype.
It produced a web.xml and beans.xml file and sample code. Besides beans.xml which remains in default state, I have modified these entities as follows:
web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/beans.xml</param-value>
</context-param>
<context-param>
<param-name>shiroConfigLocations</param-name>
<param-value>WEB-INF/shiro.ini</param-value>
</context-param>
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
and my Shiro.ini file looks like this:
# =======================
# Shiro INI configuration
# =======================
[main]
authc = org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
[users]
o = o, OPERATOR
a = a, ADMIN
s = s, SUPERVISOR
[roles]
SUPERVISOR = *
ADMIN = sayHiAdmin
OPERATOR = deleteAccounts
My simple webservice code is as follows:
import javax.jws.WebService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
#WebService(endpointInterface = "org.myCo.com.CxfShiroSecuredService.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
public String sayHi(String text) {
if (isAuthorized("sayHi")) {
return "Successfully said hi " + text;
}
if (hasRole("OPERATOR")){
return "User is OPERATOR";
}
if (hasRole("ADMIN")){
return "User is OPERATOR";
}
throw new UnauthorizedException("Logged user does not have OPERATOR's permission");
}
public String sayHiAdmin(String text) {
if (isAuthorized("sayHiAdmin")) {
return "Successfully said hi Admin " + text;
}
throw new UnauthorizedException("Logged user does not have ADMIN permission");
}
public String deleteAccounts(String text) {
if (isAuthorized("deleteAccounts")) {
return "Successfully deleted accounts " + text;
}
throw new UnauthorizedException("Logged user does not have SUPERVISOR permission");
}
private Boolean isAuthorized(String operation){
Subject currentUser = SecurityUtils.getSubject();
return currentUser.isPermitted(operation); //currentUser.isAuthenticated(); // && currentUser.isPermitted(operation);
}
private Boolean hasRole(String role){
Subject currentUser = SecurityUtils.getSubject();
return currentUser.hasRole(role);
}
}
I have a C# test client that passes authentication information in the SOAP header before invoking webservice like so:
private void OnButtonClick(object sender, RoutedEventArgs e)
{
var client = new HelloWorldClient();
var response = "";
using (new OperationContextScope(client.InnerChannel))
{
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " +
Convert.ToBase64String(Encoding.ASCII.GetBytes(UserName.Text + ":" + Password.Text));
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
try
{
response = client.sayHi("hi " + UserName.Text);
}
catch (TimeoutException tex)
{
response = tex.Message;
}
catch (CommunicationException cex)
{
response = cex.Message;
}
}
TextBox.Text = response;
}
I have used this same strategy for other web services that require Basic authentication
before invoking method calls with success but this service does not seem to be recognizing my credentials. For each method call invoked, regardless of username/password combination, I get the UnAuthorizedException thrown. Can someone shed me some light?
Thanks in advance.
You need a [urls] section in your shiro.ini file. Something like this:
[urls]
/** = authc
Check out the documentation for further details here.