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
Related
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
This may be stupid question, but i want to know the root cause.
I am implementing a simple webservice using jersey. I have declared a global variable i and initialised to 0;
I set i to 10 in sayPlainTextHello() method but when i display i in sayXMLHello() it is still 0 ; What is the reason behind this behavior.
server side code
#Path("/hello")
public class Hello
{
int i = 0;
#GET
#Path("/string")
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello()
{
i = 10;
System.out.println("string hello");
System.out.println("i in string " + i);
return "Hello";
}
#GET
#Path("/xml")
#Produces(MediaType.TEXT_XML)
public String sayXMLHello()
{
System.out.println("i in xml"+i);
System.out.println("xml hello");
return "<?xml version=\"1.0\"?>" + "<hello> Hello Jersey" + " </hello>";
}
This is my web.xml file
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>com.vogella.jersey.first</display-name>
<welcome-file-list>
<welcome-file>home.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class> com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<!-- Register resources and providers under com.vogella.jersey.first package. -->
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.spaneos.webservices</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
</web-app>
The default scope of resource classes is per-request (or request scoped). For each request a new resource instance will created to handle that particular request.
You can make your classes singleton scoped, meaning only one object will be created for the entire application, but that may not be desired for a few reasons.
If you do want to make your resource class singleton scoped, then you can just annotate your Hello class with #com.sun.jersey.spi.resource.Singleton
That said, unless you have a direct requirement to make the resource class stateful, I would aim to keep the classes stateless.
See more at Life-cycle of Root Resource Classes
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
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/