Thursday, 5 January 2012
Implementing RESTful web services using Spring In Java
Couple of weeks back my collegue Phil M implemented spring rest implementation in our project and i thought of sharing with everyone.
We can use Spring APIs and annotations to build RESTful web services seamlessly.
Other alternatives to implement RESTful web service is to use JSR 311(JAX-RS) and its implementation Jersey else we may also use Restlet framework.
The Jaxb2Marshaller offers a classesToBeBound property, which allows you to set an array of classes to be supported by the marshaller.
With the representational state transfer (REST) style architecture consists of client and server and requests and responses are built around the transfer of representations of resources. Resources are identified by global IDs that typically use a uniform resource identifier (URI). Client applications use HTTP
methods (such as GET, POST, PUT, or DELETE) to manipulate the resource or collection of resources.
For example, GET http://host/context/dept/12345 gets the particular department details with the ID 12345. The response representation could be XML or it could be a JSP/HTML page.
Annotations such as @RequestMapping and @PathVariable, are used to support resource identification and URI mappings.
Please Note That I used Spring 3.0.4 Release.
Please find one example below
Server Side
demo.domain.address.AddressSearch.java
@XmlRootElement(name = "addresses")
public class AddressSearch implements Serializable {
private List<Address> addresses;
private String errorMessage;
private boolean validAddress;
...implement get set methods
demo.domain.address.Address.java
@XmlRootElement(name = "address")
public class Address implements Serializable {
private String address1;
private String address2;
private String city;
private String county;
private String postalCode;
ersion="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" 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/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">
<wls:context-root>addressTEST</wls:context-root>
<wls:session-descriptorostalCode, @PathVariable String address1) throws Exception
{
AddressSearch addressSearch = addressService.getAddress(address1, postalCode);
return new ModelAndView(XML_VIEW_NAME, "addresses", addressSearch);
}
.........
}
AddressServiceImpl.java
@Service
public class AddressServiceImpl implements IQASService {
public AddressSearch getAddress(String houseNameOrNumber, String postalCode) {
@SuppressWarnings("rawtypes")
List addresses = new ArrayList();
AddressSearch as = new AddressSearch();
try {
addresses = ... your logic left to you how u get the data back maybe bring it from database or user another webservice etc...
} catch (Exception e) {
}
return as; // please note we have some internal logic to populate the list into a business object.
}
.....
}
web.xml
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
ramework.org/senera/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>qas</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
weblogic.xml
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" 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/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">
<wls:context-root>addressTEST</wls:context-root>
<wls:session-descriptor>
<wls:persistent-store-type>replicated_if_clustered</wls:persistent-store-type>
</wls:session-descriptor>
</wls:weblogic-web-app>
demo-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="addressService" class="demo.service.impl.AddressServiceImpl" />
<context:component-scan base-package="demo.service" />
</beans>
demo-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Register the DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter -->
<mvc:annotation-driven />
<!-- Component scan for controller classes -->
<context:component-scan base-package="demo.controller" />
<!-- Defines the marshaller/unmarshaller that uses JAXB 2 to do the object XML mapping (OXM) -->
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>demo.domain.address.AddressSearch</value>
</list>
</property>
</bean>
<!-- Defines an XML representation view that utilizes the Jaxb2Mashaller
//XML_VIEW_NAME in controller equals address, which is the view name defined in demo-servlet.xml
-->
<bean id="addresses" class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg ref="jaxbMarshaller" />
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<!--
REST services can produce different representations according to the request.
Spring 3 introduces a new view resolver called ContentNegotiatingViewResolver.
It can switch view resolvers according to request content type (the Accept property in the request header) or URI suffix.
-->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="html" value="text/html"/>
</map>
</property>
<property name="viewResolvers">
<list>
<!-- Defines a view resolver using the bean name that the user specifies
The definition shows support for handling two request content types: application/xml and text/html.
The code also defines two view resolvers: one BeanNameViewResolver to handle application/xml
and one UrlBasedViewResolver to handle text/html.
When we enter http://<host>:<port>/<appcontext>/addressTEST/getAddress/{postalCode}/{address1} in the browser it requests text/html
for the employees. Then UrlBasedViewResolver will take effect and Spring will pick /WEB-INF/jsp/addresses.jsp
as its view.
When you add the request header Accept:application/xml and invoke the request, the BeanNameViewResolver will take effect.
Per the code in AddressSearchController.java getAddressPcHouseNoName() method, it will use a view named addresses to represent,
which is the JAXB 2 marshaller view that was defined.
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
</bean>
</beans>
Please note that all the code described above can be packaged into a seperate war file and make it available in the client side
JUNIT Client Side :-
Now below Junit Class will help us to do some Unit Testing of the Controller.
TestAddressSearchController.java
public class TestAddressSearchController extends TestBaseController {
@Autowired
private RestTemplate restTemplate;
private static final String BASE_URL = "http://localhost:7001/addressTEST";
@Override
public void setUp() throws Exception {
super.setUp();
}
@Test
public void testGetAddressPcHouseNoName() {
String url = BASE_URL + "/getAddress/{postalCode}/{address1}";
postalCode = "NE28 9TA";
address1 = "165";
A//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">
<wls:context-root>addressTEST</wls:context-root>
<wls:session-descriptor8String, Class, String...)
HEAD headForHeaders(String, String...)
OPTIONS optionsForAllow(String, String...)
POST postForLocation(String, Object, String...)
PUT put(String, Object, String...)
So to recap getForObject() will perform a GET, convert the HTTP response into an object type of your choice, and returns that object.
*/
AddressSearch as = restTemplate.getForObject(url,AddressSearch.class,mapParams);
assertTrue(as.getAddresses().size() == 1);
}
...
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:spring/demo-service.xml",
"classpath:spring/demo-servlet.xml",
"classpath:spring/demo-test-rest.xml"})
public class TestBaseController {
protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
protected MockHttpSession session;
protected final static String GET = "GET";
protected final static String POST = "POST";
@Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.session = new MockHttpSession();
}
@After
public void tearDown() throws Exception {
}
}
demo-test-rest.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Rest Template -->
<!--
The RestTemplate is the central Spring class for client-side HTTP access.
RestTemplate is thread-safe once constructed, and that you can use callbacks to customize its operations.
-->
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters" ref="messageConverter" />
</bean>
<!--
HttpMessageConverters Objects passed to and returned from the methods getForObject(), postForLocation(), and put() and are converted to HTTP requests and from HTTP responses by HttpMessageConverters.
-->
<bean id="messageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="jaxbMarshaller" />
<property name="unmarshaller" ref="jaxbMarshaller" />
</bean>
</beans>
Hope this example helps.
References
http://blog.springsource.org/2009/03/27/rest-in-spring-3-resttemplate/
http://www.ibm.com/developerworks/web/library/wa-spring3webserv/
http://static.springsource.org/spring-ws/site/reference/html/oxm.html
http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch18s02.html
http://en.wikipedia.org/wiki/Representational_State_Transfer
Monday, 8 August 2011
Working With JUnit And Mock Objects
Introduction:
EasyMock works by implementing the proxy pattern. When you create a mock object, it creates a proxy object that takes the place of the real object. The proxy object gets it's definition from the interface you pass when creating the mock. We will define what methods are called and their returns from within our test method itself.
When creating a mock object, there are two types, a mock and a strict mock. In either case, our test will tell the mock object what method calls to expect and what to return when they occur. A basic mock will not care about the order of the execution of the methods. A strict mock, on the other hand, is order specific. Your test will fail if the methods are executed out of order on a strict mock.
In this example, we will be using a strict mock.
private LoginServiceImpl service;
private UserDAO mockDao;
@Override
public void setUp() {
service = new LoginServiceImpl();
mockDao = createStrictMock(UserDAO.class);
service.setUserDAO(mockDao);
}
User results = new User();
String userName = "testUserName";
String password = "testPassword";
String passwordHash = "someunecryptedpass";
expect(mockDao.loadByUsernameAndPassword(eq(userName), eq(passwordHash))).andReturn(results);
replay(mockDao);
assertTrue(service.login(userName, password));
verify(mockDao);
expect(<<method name>>)
This is a call to the static method EasyMock.expect. It tells your mock object to expect the method loadByUsernameAndPassword to be called.
eq(userName), eq(passwordHash)
When EasyMock compares the values passed to the method call, it does and == comparison. The eq comparator in this case will compare the contents of the string using it's .equals method.
.andReturn(results);
This tells our mock object what to return after this method is called.
We can as well use expectAndReturn() single method call instead of calling expect() and andReturn() method seperatly.
The final three lines are the ones that do the testing work. replay(mockDao); tells EasyMock "We're done declaring our expectations. It's now time to run what we told you".
Finally, verify(mockDao); tells EasyMock to validate that all of the expected method calls were executed and in the correct order.
Example 1:
Let's say we have a PageCachingFilter which will add few response headers to remove caching.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletResponse sResponse = (HttpServletResponse)response;
sResponse.addHeader("Cache-Control","no-cache");
sResponse.addHeader("Pragma","no-cache");
chain.doFilter(request, response);
}
Our TestCase will look something like below
public void testCacheHeaders()
{
// Create mock controllers
MockControl requestControl = MockControl.createControl(HttpServletRequest.class);
MockControl responseControl = MockControl.createControl(HttpServletResponse.class);
MockControl filterChainControl = MockControl.createControl(FilterChain.class);
// Create the mock objects
HttpServletRequest request = (HttpServletRequest)requestControl.getMock();
HttpServletResponse response = (HttpServletResponse)responseControl.getMock();
FilterChain filterChain = (FilterChain)filterChainControl.getMock();
// Record the expectation
response.addHeader("Cache-Control","no-cache");
response.addHeader("Pragma","no-cache");
try
{
filterChain.doFilter(request, response);
}
catch(Exception e)
{
fail(e.getMessage());
}
// Stop recording
requestControl.replay();
responseControl.replay();
filterChainControl.replay();
// Execute the test
Filter filter = new PageCachingFilter();
try
{
filter.init(null);
filter.doFilter(request, response, filterChain);
filter.destroy();
}
catch(Exception e)
{
fail(e.getMessage());
}
// Verify the expectations against the test
requestControl.verify();
responseControl.verify();
filterChainControl.verify();
}
IMPORTANT NOTE 1: Please note that spring helps a bit further and eases mock object creation of HttpServletRequest,Response and FilterChain objects.
Hence ideally in a single step we can create the mock object unlike the 2 steps described above.
import org.easymock.MockControl;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public void setUp()
{
filter = new UserFilter();
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
session = new MockHttpSession();
chain = new MockFilterChain();
Principal principal = new Principal() {
public String getName()
{
return "TheName";
}};
request.setUserPrincipal(principal);
}
IMPORTANT NOTE 2: We can also set the return value of a mock object using setReturnValue() method.
Example:
User user = new User();
user.setName("TheName");
MockControl userServiceControl = MockControl.createControl(IUserService.class);
userServiceControl.setReturnValue(user);
IMPORTANT NOTE 3:
Below code will show us how to create a Mock ServletContext which can accept the file names which we normally mention in web.xml as a parameter to get loaded.
servletContext = new MockServletContext();
String configFiles = "/WEB-INF/config/framework/config.xml ";
servletContext.addInitParameter("CONFIG_FILE", configFiles);
Below code will show us how to create a StaticWebApplicationContext and attach it to the created ServletContext.
Servlet Context is known can be created from the above code.
StaticWebApplicationContext applicationContext = new StaticWebApplicationContext();
applicationContext.setServletContext(servletContext);
applicationContext.refresh();
applicationContext.start();
servletContext.setAttribute(XmlWebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);
IMPORTANT NOTE 4:
If we want to test spring 3.0 front-end controllers below is sample code
import org.junit.Test;
import org.springframework.web.servlet.ModelAndView;
import static org.junit.Assert.assertNotNull;
public class TestHomeController extends BaseTestController {
private HomeController controller;
private final static String URI = "/home.html";
@Test
public void testHomeController() throws Exception {
request.setRequestURI(URI);
this.controller = new HomeController();
ModelAndView view = controller.home(request,response);
assertNotNull(view);
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:spring/demo-servlet.xml"})
@TransactionConfiguration(transactionManager = "transactionManager")
public class BaseTestController {
protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
protected MockHttpSession session;
@Before
public void setUp() throws Exception {
System.out.println("Doing set-up");
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
}
@After
public void tearDown() throws Exception {
System.out.println("Doing tearDown");
}
}
demo-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">property name="maxUploadSize" value="10000000"/>
</bean>
....
</beans>
References
httstrong>
<!-- Maximum file size in bytes. -->
<property name="maxUploadSize" value="10000000"/>
</bean>
....
</beans>
References
http://www.michaelminella.com/testing/unit-testing-with-junit-and-easymock.html
http://www.easymock.org/Downloads.html
http://easymock.org/api/easymock/2.4/index.html?org/easymock/MockControl.html
Wednesday, 3 August 2011
IMPLEMENTING AJAX FUNCTIONALITY USING JQUERY + JSON FRAMEWORK
In our old project we are using json framework to achieve ajax functionality.
PLEASE DOWNLOAD THE REQUIRED JSON JAR FILE . WE ARE USING "json-rpc-1.0.jar" JAR FILE.
I want to share the code which can be useful for the readers.
Let us say we want to re-populate the select option dropdown when user changes a value in the existing dropdown.
In the html form contains below code:
<select id="demoSelect" name="demoSelect">
<option value="1">One</option>
<option value="2">Two</option>
</select>
JavaScript Code implementing json looks like below
$("select[name^='demoSelect']").bind('change', function(event)
{
//var e = document.getElementById("demoSelect"); //Get the actual element
var id = event.target.id; //Get the id of the LOV that was changed
var e = document.getElementById(id); //Get the actual element
var i = e.selectedIndex;
var t = e.options[i].text; //Get the text
alert("came here...." + t);
var frmdata = {taskStatusTypeName : t} ;
var myurl = 'demoServlet';
alert(myurl);
var options = {
type: 'GET',
url: myurl,
data: frmdata,
cache: false,
dataType: 'json',
error: function(xhr, textStatus,errorThrown){
alert('An error occurred! ' + errorThrown) ;
},
success: function(data,textStatus){
alert("came here..........success");
var elName = '#demoSelect';
var reasonSelect = $(elName).children().remove().end() ;
for (var i=0;i<data.length;i++){
if (i==0){
reasonSelect.append('<option value="' + data[i].key + '" selected="true">' + data[i].value + '</option>') ;
/* var noteName = '#newNote_' + index ;
var noteVal = data[i].value ;
$(noteName).val(noteVal) ;
*/
}
else{
reasonSelect.append('<option value="' + data[i].key + '">' + data[i].value + '</option>') ;
}
}
$(elName).focus(); //Set focus to the new note input
}
} ;
$.ajax(options) ;
});
As correctly observed it tries to call the underlying backend servlet to fetch the data.
Our servlet code goes like this and donot forget to add the entries in web.xml !
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
public class DemoJsonServiceServlet extends HttpServlet{
private static final String CONTENT_TYPE = "application/json" ;
public DemoJsonServiceServlet() {
}
private static Logger _logger = Logger.getLogger(DemoJsonServiceServlet.class);
public void init(ServletConfig config) throws ServletException
{
super.init(config);
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String taskStatusTypeName = request.getParameter("taskStatusTypeName");
response.setContentType(CONTENT_TYPE) ;
response.setHeader("Cache-Control","no-cache") ;
response.getWriter().write(getJson(request,taskStatusTypeName)) ;
}
private String getJson(HttpServletRequest request,String taskStatusTypeName) throws ServletException {
if (_logger.isDebugEnabled()){
_logger.debug("Task status type " + taskStatusTypeName) ;
}
try {
JSONArray jsonKeyValues = new JSONArray() ;
for(int i=0;i<10;i++) {
JSONObject jsonObj = new JSONObject() ;
jsonObj.put("key","key" +i) ;
jsonObj.put("value","value" +i) ;
jsonKeyValues.put(jsonObj) ;
}
return jsonKeyValues.toString() ;
} catch (Exception e) {
_logger.error("Error in Json service" + e.getMessage()) ;
throw new ServletException(e);
}
}
}
Hope this explanation helps.
Thursday, 21 July 2011
Handling Forbidden Pages in SpringMVC3.0 using annotations
@Controller
@RequestMapping(value = "/Login/**")
public class LoginController extends CommonController {
public static final String loginError = "/Login/LoginError";
@RequestMapping(value="/Login.html")
public String login() {
return Globals.login;
}
@RequestMapping(value="/LoginError.html")
public String loginError() {
return loginError;
}
}
Tiles Definition For "/accessDenied"
<definition name="/accessDenied" extends="base">
<put-attribute name="subtitle" value="Login Error" />
<put-attribute name="menu" value="/WEB-INF/tiles/templates/loginMenu.jsp" />
<put-attribute name="body" value="/WEB-INF/jsp/accessDenied/showAccessDenied.jsp" />
</definition>
showAccessDenied.jsp
<p>
Access to the specified resource has been denied.
<br />
Click <a href="<c:url value=''/>"><b><u>here</u></b></a> to try again.
</p>
<!--
For reasons why we set response status=200 refer below link
http://www.coderanch.com/t/365499/Servlets/java/web-xml-error-page-not
-->
Hope this explanation helps.
Wednesday, 6 July 2011
Spring Security3.5 Authorisation Cache Problem
In Our project We are using spring security 3.5 for authorisation purpose only.
We use j2ee container for authentication and donot rely on spring for authentication and logout purposes.
Today we faced a cache problem with regards to authentication details.
For the first time if user A logs in his authentication details i.e his associated roles(granted authorities) are correctlly populated.
But when we try to logout user A and make user B to login due to some cache problem user A authentication details i.e his associated roles(granted authorities) are getting displayed when user B login in :-(
It means for spring though user B is logged in it still assumes user A is logged in which means the logout functionality went for a toss and we are not properly implemented the logout functionality.
As said earlier we are not replying on spring for built-in logout functionality.
We have resolved the problem by mimcing the spring builtin logout functionality and changing code when required especially "filterProcessesUrl" variable in LogoutFilter class. Initially it will have value something like "j_securitycheck_logout" but we changed to our project specific logout URL.
Just pasting code for the benefit of other developers.
spring security.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Sample namespace-based configuration
-
-->
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<global-method-security pre-post-annotations="enabled">
</global-method-security>
<http auto-config="true" use-expressions="true" access-denied-page="/accessDenied/showAccessDenied.html">
</http>
<!--
The final servlet filter in the default Spring Security filter chain,
FilterSecurityInterceptor, is the filter responsible for coming up with a
decision on whether or not a particular request will be accepted or denied. At this
point the FilterSecurityInterceptor filter is invoked, the principal has already
been authenticated, so the system knows that they are valid users.
-->
<b:bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map path-type="ant">
<filter-chain pattern="/**" filters="exceptionTranslationFilter,securityContextPersistenceFilter,j2eePreAuthFilter,fsi,logoutFilter"/>
</filter-chain-map>
</b:bean>
<b:bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<b:property name="forceEagerSessionCreation" value="true"/>
</b:bean>
<b:bean id="logoutFilter" class="demo.LogoutFilterWrapper">
</b:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref='preAuthenticatedAuthenticationProvider'/>
</authentication-manager>
<b:bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<b:property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService"/>
</b:bean>
<b:bean id="preAuthenticatedUserDetailsService"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService"/>
<b:bean id="j2eePreAuthFilter" class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
<b:property name="authenticationManager" ref="authenticationManager"/>
<b:property name="authenticationDetailsSource" ref="authenticationDetailsSource"/>
<b:property name="continueFilterChainOnUnsuccessfulAuthentication" value="false"/>
</b:bean>
<b:bean id="authenticationDetailsSource" class="org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource">
<b:property name="mappableRolesRetriever" ref="j2eeMappableRolesRetriever"/>
<b:property name="userRoles2GrantedAuthoritiesMapper" ref="j2eeUserRoles2GrantedAuthoritiesMapper"/>
</b:bean>
<b:bean id="j2eeMappableRolesRetriever" class="org.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever">
<!-- <property name="webXmlInputStream"><bean factory-bean="webXmlResource" factory-method="getInputStream"/>
</property> -->
</b:bean>
<b:bean id="webXmlResource" class="org.springframework.web.context.support.ServletContextResource">
<b:constructor-arg ref="servletContext"/>
<b:constructor-arg value="/WEB-INF/web.xml"/>
</b:bean>
<b:bean id="servletContext" class="org.springframework.web.context.support.ServletContextFactoryBean"/>
<b:bean id="j2eeUserRoles2GrantedAuthoritiesMapper" class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
<b:property name="attributePrefix" value="dummy"/>
</b:bean>
<!-- AffirmativeBased== If any voter grants access, access is immediately granted,regardless of previous denials. -->
<!-- decisionVoters property => This property is auto-configured until we declare our own AccessDecisionManager. The default
AccessDecisionManager requires us to declare the list of voters who are consulted
to arrive at the authentication decisions. -->
<b:bean id="httpRequestAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<b:property name="allowIfAllAbstainDecisions" value="false"/>
<b:property name="decisionVoters">
<b:list>
<b:ref bean="roleVoter"/>
<b:ref bean="webExpressionVoter"/>
</b:list>
</b:property>
</b:bean>
<!-- roleVoter ==>
Checks that the user has the
GrantedAuthority matching
the declared role. Expects the
access attribute to define
a comma-delimited list of
GrantedAuthority names. The
ROLE_ prefix is expected, but
optionally configurable.
webExpressionVoter=> SpEL handling is supplied by a different Voter
implementation, o.s.s.web.access.expression.WebExpressionVoter,
which understands how to evaluate the SpEL expressions.
The <filter-security-metadata-source> element is responsible for
configuring the SecurityMetadataSource implementation used by the
FilterSecurityInterceptor, including the URL declarations and roles
required to access them.
-->
<b:bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
<b:bean id="webExpressionVoter" class="org.springframework.security.web.access.expression.WebExpressionVoter"/>
<b:bean id="fsi" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<b:property name="authenticationManager" ref="authenticationManager"/>
<b:property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<b:property name="securityMetadataSource">
<filter-invocation-definition-source use-expressions="true">
<!-- <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/css/**" filters="none"/>
<intercept-url pattern="/login.jsp*" filters="none"/>
<intercept-url pattern="/**" access="hasRole('sls')"/>
-->
</filter-invocation-definition-source>
</b:property>
</b:bean>
<!-- ExceptionTranslationFilter, one of
the last servlet filters in the standard Spring Security filter chain, is responsible for
examining exceptions thrown during the authentication and authorization processes
(in FilterSecurityInterceptor, the culmination of the filter chain), and reacting
appropriately to them. -->
<b:bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<b:property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
<b:property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</b:bean>
<b:bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<b:property name="loginFormUrl" value="/login.jsp"/>
</b:bean>
<b:bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<b:property name="errorPage" value="/accessDenied/showAccessDenied.html"/>
</b:bean>
<b:bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"/>
</b:beans>
LogoutFilterWrapper.java
import java.io.IOException;
import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
public class LogoutFilterWrapper implements Filter {
public static final Logger LOGGER = Logger.getLogger(LogoutFilterWrapper.class);
private String logoutSuccessfulUrl="/Login/Login";
private String logoutSuccessfulInactivityUrl="/Login/Login";
private LogoutFilter filter;
protected void initialize() {
LOGGER.debug("LogoutFilterWrapper:Entered into initialize method");
final SecurityContextLogoutHandler context = new SecurityContextLogoutHandler();
context.setInvalidateHttpSession( true );
this.filter =new LogoutFilter( new CustomLogoutSuccessHandler(), new LogoutHandler[] { context } );
}
public void setLogoutSuccessfulUrl(String inUrl ) {
this.logoutSuccessfulUrl = inUrl;
}
public void setLogoutSuccessfulUrlInactivity(String inUrl ) {
this.logoutSuccessfulInactivityUrl = inUrl;
}
@Override
public final void init(FilterConfig inFilterConfig ) throws ServletException {
LOGGER.debug("LogoutFilterWrapper:Entered into init method");
initialize();
this.filter.init(inFilterConfig);
}
@Override
public void destroy() {
this.filter.destroy();
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
LOGGER.debug("LogoutFilterWrapper:Entered into doFilter method");
if(this.filter==null) {
initialize();
}
this.filter.doFilter( arg0, arg1, arg2 );
}
/**
* Success Handler
*/
private class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String targetUrl = "";
SecurityContextHolder.getContext().setAuthentication(null);
request.getSession().removeAttribute(Globals.USER);
request.getSession().invalidate();
redirectStrategy.sendRedirect( request, response, targetUrl );
}
}
}
LogoutFilter.java
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
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 org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
/**
* Logs a principal out.
* <p>
* Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order they are required.
* Generally you will want to call logout handlers <code>TokenBasedRememberMeServices</code> and
* <code>SecurityContextLogoutHandler</code> (in that order).
* <p>
* After logout, a redirect will be performed to the URL determined by either the configured
* <tt>LogoutSuccessHandler</tt> or the <tt>logoutSuccessUrl</tt>, depending on which constructor was used.
*
* @author Ben Alex
*/
public class LogoutFilter extends GenericFilterBean {
public static final Logger LOGGER = Logger.getLogger(LogoutFilter.class);
//~ Instance fields ================================================================================================
private String filterProcessesUrl = "/LogOut.html";
private List<LogoutHandler> handlers;
private LogoutSuccessHandler logoutSuccessHandler;
//~ Constructors ===================================================================================================
/**
* Constructor which takes a <tt>LogoutSuccessHandler</tt> instance to determine the target destination
* after logging out. The list of <tt>LogoutHandler</tt>s are intended to perform the actual logout functionality
* (such as clearing the security context, invalidating the session, etc.).
*/
public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler,LogoutHandler... handlers) {
Assert.notEmpty(handlers, "LogoutHandlers are required");
this .handlers = Arrays.asList(handlers);
Assert.notNull(logoutSuccessHandler,
"logoutSuccessHandler cannot be null");
this .logoutSuccessHandler = logoutSuccessHandler;
}
public LogoutFilter(String logoutSuccessUrl,LogoutHandler... handlers) {
Assert.notEmpty(handlers, "LogoutHandlers are required");
this .handlers = Arrays.asList(handlers);
Assert.isTrue(!StringUtils.hasLength(logoutSuccessUrl)
|| UrlUtils.isValidRedirectUrl(logoutSuccessUrl),
logoutSuccessUrl + " isn't a valid redirect URL");
SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
if (StringUtils.hasText(logoutSuccessUrl)) {
urlLogoutSuccessHandler
.setDefaultTargetUrl(logoutSuccessUrl);
}
logoutSuccessHandler = urlLogoutSuccessHandler;
}
//~ Methods ========================================================================================================
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
LOGGER.debug("LogoutFilter doFilter entered");
if (requiresLogout(request, response)) {
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
LOGGER.debug("Logging out user '" + auth + "' and transferring to logout destination");
for (LogoutHandler handler : handlers) {
handler.logout(request, response, auth);
}
logoutSuccessHandler.onLogoutSuccess(request, response,auth);
return;
}
chain.doFilter(request, response);
}
/**
* Allow subclasses to modify when a logout should take place.
*
* @param request the request
* @param response the response
*
* @return <code>true</code> if logout should occur, <code>false</code> otherwise
*/
protected boolean requiresLogout(HttpServletRequest request,
HttpServletResponse response) {
String uri = request.getRequestURI();
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
public void setFilterProcessesUrl(String filterProcessesUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl),
filterProcessesUrl + " isn't a valid value for"
+ " 'filterProcessesUrl'");
this .filterProcessesUrl = filterProcessesUrl;
}
protected String getFilterProcessesUrl() {
return filterProcessesUrl;
}
}
For logout we use common spring action calls
our jsp contain the call :
<a href="<c:url value='/LogOut.html'/>">Log Out</a>
@RequestMapping(value="/LogOut.html")
public ModelAndView logOut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SecurityContextHolder.getContext().setAuthentication(null);
request.getSession().removeAttribute(Globals.USER);
request.getSession().invalidate();
return new ModelAndView(Globals.login);
}
Thursday, 23 June 2011
Spring Security3.0.x Authorisation Only using J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource
We have a requirement in which we use weblogic container for authentication and we are planning to use spring security for authorisation only.
We are using weblogic11g app server and spring security 3.5 for achieving the same.
Below code can help to map j2ee user roles to Spring GrantedAuthorities.
I even posted my question in below website to clear off my doubts.
http://forum.springsource.org/showthread.php?110397-How-to-achieve-Spring-Security3.5-for-authorisation-only-and-not-for-authentication
-------------------------------------------------------------------------------------------------------------------------------------------------
Main things required in your web.xml
-------------------------------------------------------------------------------------------------------------------------------------------------
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-security.xml
/WEB-INF/servlets.xml
/WEB-INF/views.xml
</param-value>
</context-param>
<servlet>
<servlet-name>springSecurityPreAuth</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/servlets.xml
/WEB-INF/views.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springSecurityPreAuth</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Secure TestWeb Access</web-resource-name>
<url-pattern>*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>testWebApp</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
<security-role>
<role-name>testWebApp</role-name>
</security-role>
-------------------------------------------------------------------------------------------------------------------------------------------------
weblogic.xml
-------------------------------------------------------------------------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" 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/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">
<wls:weblogic-version>10.3.4</wls:weblogic-version>
<wls:context-root>commonloginweblogic</wls:context-root>
<wls:security-role-assignment>
<wls:role-name>testWebApp</wls:role-name>
<wls:principal-name>testWebApp</wls:principal-name>
</wls:security-role-assignment>
</wls:weblogic-web-app>
-------------------------------------------------------------------------------------------------------------------------------------------------
Your applicationContext-security.xml code goes like this
-------------------------------------------------------------------------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Sample namespace-based configuration
-
-->
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<global-method-security pre-post-annotations="enabled">
</global-method-security>
<http auto-config="true" use-expressions="true">
</http>
<b:bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map path-type="ant">
<filter-chain pattern="/**" filters="j2eePreAuthFilter"/>
</filter-chain-map>
</b:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref='preAuthenticatedAuthenticationProvider'/>
</authentication-manager>
<b:bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<b:property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService"/>
</b:bean>
<b:bean id="preAuthenticatedUserDetailsService"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService"/>
<b:bean id="j2eePreAuthFilter" class="security.SsoUserJ2eePreAuthenticatedProcessingFilter">
<b:property name="authenticationManager" ref="authenticationManager"/>
<b:property name="authenticationDetailsSource" ref="authenticationDetailsSource"/>
<b:property name="continueFilterChainOnUnsuccessfulAuthentication" value="false"/>
</b:bean>
<b:bean id="authenticationDetailsSource" class="org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource">
<b:property name="mappableRolesRetriever" ref="j2eeMappableRolesRetriever"/>
<b:property name="userRoles2GrantedAuthoritiesMapper" ref="j2eeUserRoles2GrantedAuthoritiesMapper"/>
</b:bean>
<b:bean id="j2eeMappableRolesRetriever" class="org.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever">
</b:bean>
<b:bean id="j2eeUserRoles2GrantedAuthoritiesMapper" class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
<b:property name="attributePrefix" value="test"/>
</b:bean>
</b:beans>
-------------------------------------------------------------------------------------------------------------------------------------------------
Our class extending J2eePreAuthenticatedProcessingFilter code goes like this
-------------------------------------------------------------------------------------------------------------------------------------------------
package security;
import java.security.Principal;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
import domain.User;
import view.SessionState;
/**
* This filter implementation hooks into the Spring Security authentication
* mechanism and ensures that the additional SSO secrity details are loaded and
* made available in the session.
*/
public class SsoUserJ2eePreAuthenticatedProcessingFilter extends J2eePreAuthenticatedProcessingFilter {
/**
* Log.
*/
private static final Logger LOG = Logger.getLogger(SsoUserJ2eePreAuthenticatedProcessingFilter.class);
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
LOG.debug("successfulAuthentication()");
// There may be unauthenticated threads all requesting for the same session
// at the same time so synchronisation must be achieved.
//
// To minimise contention, this implementation synchronises on the session
// object itself.
//
// Since this is only a session startup issue, there is no further
// contention, bottle-neck or performance impact.
HttpServletRequest req = (HttpServletRequest) request; //principal is set ed()) {LOG.debug("user=" + user);}
if(user != null) {
LOG.debug("Got user data");
//final Authentication authenticatcation()" + authentication);
HttpSession session = request.getSession();
synchronized(session) {
SessionState sessionState = (SessionState)session.getAttribute(SessionState.NAME);
if(sessionState == null) {
LOG.debug("Create new application session state for the newly authenticated user");
// Create new session state for this user
sessionState = new SessionState();
request.getSession().setAttribute(SessionState.NAME, sessionState);
// Load the additional user data for this user
Principal principal = request.getUserPrincipal();
String username = principal.getName();
if(LOG.isDebugEnabled()) {LOG.debug("username=" + username);}
User user=new User();
user.setUsername(username);
if(LOG.isDebugEnabled()) {LOG.debug("user=" + user);}
if(user != null) {
LOG.debug("Got user data");
//final Authentication authentication2 = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
for (GrantedAuthority authority : authentication.getAuthorities()) {
LOG.debug("roles retrieved From Spring=" + authority.getAuthority());
}
}
//
sessionState.setUser(user);
}
else {
// TODO throw exception?
LOG.warn("User authenticated but no user data available for '" + username + "'");
}
}
}
super.successfulAuthentication(request, response, authResult);
}
}
-------------------------------------------------------------------------------------------------------------------------------------------------
In our jsp's finally we can authorise the user based on role
<sec:authorize access="hasRole('testWebApp')">
You are a Junior Developer
</sec:authorize>
-------------------------------------------------------------------------------------------------------------------------------------------------
We can also access the role information in our java code.
SecurityContextHolderAwareRequestWrapper requestWrapper=new SecurityContextHolderAwareRequestWrapper(request,"test");
boolean testRole=requestWrapper.isUserInRole("Webapp");
LOGGER.debug("HomeController.handleRequest testRole()." + testRole);
-------------------------------------------------------------------------------------------------------------------------------------------------
If you want to restrict the user acccess to various links in the web application then we need to add the fsi filter as well
<b:bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map path-type="ant">
<filter-chain pattern="/**" filters="j2eePreAuthFilter,fsi"/>
</filter-chain-map>
</b:bean>
<b:bean id="httpRequestAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<b:property name="allowIfAllAbstainDecisions" value="false"/>
<b:property name="decisionVoters">
<b:list>
<b:ref bean="roleVoter"/>
<b:ref bean="webExpressionVoter"/>
</b:list>
</b:property>
</b:bean>
<b:bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
<b:bean id="webExpressionVoter" class="org.springframework.security.web.access.expression.WebExpressionVoter"/>
<b:bean id="fsi" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<b:property name="authenticationManager" ref="authenticationManager"/>
<b:property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<b:property name="securityMetadataSource">
<filter-invocation-definition-source use-expressions="true">
<intercept-url pattern="/secure/extreme/**" access="hasRole('testRoleExtreme')"/>
<intercept-url pattern="/secure/**" access="hasRole('testRoleSecure')"/>
<intercept-url pattern="/**" access="hasRole('testRole')"/>
</filter-invocation-definition-source>
</b:property>
</b:bean>
-------------------------------------------------------------------------------------------------------------------------------------------------
Now if you want to implement a proper error page whenever any security related interceptors like FilterSecurityInterceptor throws exceptions then we need to add below tags in our security.xml
If you don't add then you may face below stack trace
Error 500--Internal Server Error
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:71)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:203)
Below code should work and will resolve the issue
<b:bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map path-type="ant">
<filter-chain pattern="/**" filters="exceptionTranslationFilter,j2eePreAuthFilter,fsi"/>
</filter-chain-map>
</b:bean>
<b:bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<b:property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
<b:property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</b:bean>
<b:bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<b:property name="loginFormUrl" value="/login.jsp"/>
</b:bean>
<b:bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<b:property name="errorPage" value="/accessDenied/showAccessDenied.html"/>
</b:bean>
Remember in our jsp page add below code compulsory if you are relying on I.E and also want to redirect to user-defined error page rather than the typical I.E forbidden page.
<% response.setStatus(200); %>
Refer to this article for more details
http://www.coderanch.com/t/365499/Servlets/java/web-xml-error-page-not
For more information please refer to below url
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/core-web-filters.html
-------------------------------------------------------------------------------------------------------------------------------------------------
Hope this article helps.
Thursday, 28 April 2011
Working With Custom Oracle Object Types In IBatis
Today we got a requirement for working With Custom Oracle Object Types In IBatis.
One of our stored procedure takes a "table of object type" as one of the parameter.
Our requirement is to call this stored proceudure from IBatis.
I would like to thank the original author found in below link as i found solution only after following his instructions.
http://mail-archives.apache.org/mod_mbox/ibatis-user-java/200802.mbox/%3C8B243E70CE6BFB438E3CDE03E1E4F698925024@zil01exm62.ds.mot.com%3E
Our Java Code goes like this
One of our stored procedure takes a "table of object type" as one of the parameter.
Our requirement is to call this stored proceudure from IBatis.
I would like to thank the original author found in below link as i found solution only after following his instructions.
http://mail-archives.apache.org/mod_mbox/ibatis-user-java/200802.mbox/%3C8B243E70CE6BFB438E3CDE03E1E4F698925024@zil01exm62.ds.mot.com%3E
create or replace TYPE JOB_MEASURES_OBJ_TYPE AS OBJECT
(
MEASURE_ID NUMBER,
SERVICE_CODE VARCHAR2(30 BYTE),
QUANTITY NUMBER,
RECEIPT_DATE DATE,
COUNTER NUMBER,
JOB_ID NUMBER,
ERROR_MSG VARCHAR2(4000 BYTE),
CREATION_DATE DATE,
CREATED_BY VARCHAR2(30 BYTE),
LAST_UPDATE_DATE DATE,
LAST_UPDATED_BY VARCHAR2(30 BYTE)
);
create or replace TYPE JOB_MEASURES_TAB_TYPE AS TABLE OF JOB_MEASURES_OBJ_TYPE;
PROCEDURE pr_insert_job_measures_row
(
pi_tbl_rec IN JOB_MEASURES_TAB_TYPE,
po_message_type OUT NUMBER,
po_message OUT VARCHAR2
) IS
v_rec_index PLS_INTEGER;
BEGIN
v_rec_index := pi_tbl_rec.FIRST;
FOR v_rec_index IN pi_tbl_rec.FIRST .. pi_tbl_rec.LAST LOOP
INSERT INTO dummy
JOB_ID,
)
values
(
pi_tbl_rec(v_rec_index).JOB_ID,
);
END LOOP;
END;
IBatis Xml file entries goes like this
<typeAlias type="JobMeasuresTypeHandler" alias="JobMeasuresTypeHandler" />
<parameterMap id="insertJobMeasuresProcParameters" class="java.util.Map">
<parameter property="jobMeasuresTableRec" typeHandler="JobMeasuresTypeHandler" mode="IN" />
<parameter property="messageType" jdbcType="NUMERIC" javaType="int" mode="OUT"/>
<parameter property="message" jdbcType="VARCHAR" javaType="java.lang.String" mode="OUT"/>
</parameterMap>
<procedure id="insertJobMeasuresProc" parameterMap="JobMeasures.insertJobMeasuresProcParameters">
{call pr_insert_job_measures_row(?,?,?)}
</procedure>
Our Java Code goes like this
JobMeasureOracleDomainObject.java
public class JobMeasureOracleDomainObject implements ORAData,ORADataFactory {
private Long measureId;
private String serviceCode;
private Long quantity;
private Date receiptDate;
private Long counter;
private Long jobId;
private String errorMsg;
private Date creationDate;
private String creationBy;
private Date lastUpdatedDate;
private String lastUpdatedBy;
public static final String _SQL_NAME = "JOB_MEASURES_OBJ_TYPE";
public static final int _SQL_TYPECODE = OracleTypes.STRUCT;
protected MutableStruct _struct;
static int[] _sqlType = {OracleTypes.NUMBER, OracleTypes.CHAR,OracleTypes.NUMBER,OracleTypes.DATE,OracleTypes.NUMBER
,OracleTypes.NUMBER,OracleTypes.CHAR,OracleTypes.DATE,OracleTypes.CHAR,OracleTypes.DATE};
static ORADataFactory[] _factory = new ORADataFactory[11];
static final JobMeasureOracleDomainObject _JobMeasureFactory = new JobMeasureOracleDomainObject();
// logger (if you wish)
//private final Log log = LogFactory.getLog(JobMeasureOracleDomainObject.class);
private static final long serialVersionUID = -7710368639791237838L;
/* constructor */
protected JobMeasureOracleDomainObject(boolean init) {
if (init) {
_struct = new MutableStruct(new Object[11], _sqlType,_factory);
}
}
public JobMeasureOracleDomainObject() {
this(true);
}
/* ORAData interface */
public Datum toDatum(Connection conn) throws SQLException {
System.out.println("Calling method toDatum...");
Datum d = _struct.toDatum(conn, _SQL_NAME);
return d;
}
/* ORADataFactory interface */
public ORAData create(Datum d, int sqlType) throws SQLException {
return create(null, d, sqlType);
}
protected ORAData create(JobMeasureOracleDomainObject o, Datum d, int sqlType) throws SQLException {
System.out.println("Calling method create...");
if (d == null) {
return null;
}
if (o == null) {
o = new JobMeasureOracleDomainObject(false);
}
o._struct = new MutableStruct((STRUCT) d, _sqlType, _factory);
return o;
}
public static ORADataFactory getORADataFactory() {
return _JobMeasureFactory;
}
public Long getMeasureId() throws SQLException {
return (Long) _struct.getAttribute(0);
}
public void setMeasureId(Long measureId) throws SQLException{
this.measureId = measureId;
_struct.setAttribute(0, this.measureId);
}
........
}
JobMeasuresTypeHandler.java
public class JobMeasuresTypeHandler implements TypeHandlerCallback
{
private static final String JOB_MEASURES_TAB = "JOB_MEASURES_TAB_TYPE";
private static final String JOB_MEASURES_REC = "JOB_MEASURES_OBJ_TYPE";
static {
JdbcTypeRegistry.setType(JOB_MEASURES_REC, OracleTypes.STRUCT);
JdbcTypeRegistry.setType(JOB_MEASURES_TAB, OracleTypes.ARRAY);
};
@SuppressWarnings("unchecked")
public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
System.out.println("calling setParameter..." + parameter);
System.out.println("calling setParameter..." + parameter.getClass());
try {
List<JobMeasureOracleDomainObject> jobMeasuresList = (List<JobMeasureOracleDomainObject>) parameter;
// System.out.println("Converting list to array...");
JobMeasureOracleDomainObject[] recArray = new JobMeasureOracleDomainObject[jobMeasuresList.size()];
for (int i = 0; i < recArray.length; i++) {
recArray[i] = jobMeasuresList.get(i);
}
System.out.println("Converted list to array.");
Connection conn = setter.getPreparedStatement().getConnection();
if (conn instanceof DelegatingConnection) {
DelegatingConnection dcon = (DelegatingConnection) conn;
conn = dcon.getInnermostDelegate();
}
conn = (OracleConnection) conn;
//ARRAY array=createJobMeasuresDataARRAY(conn);
ArrayDescriptor arrayDescriptor =ArrayDescriptor.createDescriptor(JOB_MEASURES_TAB, conn);
ARRAY array = new ARRAY(arrayDescriptor, conn, recArray);
setter.setArray(array);
} catch (SQLException sqle) {
System.out.println("SQLException: " + sqle);
throw sqle;
}
}
public Object getResult(ResultGetter getter) throws SQLException {
System.out.println("calling getResult..." + getter.getObject());
return getter.getObject();
}
/**
* Nothing here can help us anyway...
*/
public Object valueOf(String arg0) {
System.out.println("calling valueOf..." + arg0);
if (arg0 == null) {
return new ArrayList<JobMeasureOracleDomainObject>();
}
return arg0;
}
}
ObjectTypeArraysJobMeasuresTest.java
public class ObjectTypeArraysJobMeasuresTest {
public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {
new ObjectTypeArraysJobMeasuresTest().insertJobMeasuresProcParameters();
}
private void insertJobMeasuresProcParameters() throws IOException, SQLException, ClassNotFoundException {
//Connection conn = null;
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
List<JobMeasureOracleDomainObject> jobMeasuresList=new ArrayList<JobMeasureOracleDomainObject>();
JobMeasureOracleDomainObject jobMeasureOracleDomainObject=new JobMeasureOracleDomainObject();
jobMeasureOracleDomainObject.setJobId(999L);
jobMeasureOracleDomainObject.setMeasureId(999L);
jobMeasuresList.add(jobMeasureOracleDomainObject);
Map parameterMap = new HashMap();
parameterMap.put("jobMeasuresTableRec", jobMeasuresList);
sqlMap.update("JobMeasures.insertJobMeasuresProc", parameterMap);
Integer resultCode = (Integer) parameterMap.get("messageType");
String resultMessage = (String)parameterMap.get("message");
System.out.println(resultCode + ":" + resultMessage);
}
}
Subscribe to:
Posts (Atom)