open rest endpoint with spring security - spring

I am a mobile developer, and a green horn with Spring Security, but any case it was working fine in my web app. But now I am stuck with an issue.
Lets suppose my app context is x and I want to allow everybody to use a rest service endpoint like
https://localhost:8443/x/12
This endpoint is defined in a Controller in this way:
#Secured("permitAll")
#RequestMapping("/{id}")
public ModelAndView foo(#PathVariable("id") int id, HttpServletRequest request) {
Pet existingPet = petService.getPet(id);
ModelAndView mav = new ModelAndView();
if (null != existingPet) {
mav.addObject("petAttribute", existingPet);
mav.setViewName("b-show-pet");
}else{
mav.setViewName("b-accessdenied");
}
return mav;
}
In my web.xml I have
<servlet-mapping>
<servlet-name>loc-app</servlet-name>
<url-pattern>/auth/**</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>loc-app</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<!-- Enable Spring Security -->
<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>
Now, IMO an URI like
https:/localhost:8443/x/15
should be intercepted by spring security, but the annotation #Secured("permitAll") above the service in the controller should be enable access. But it is not working. So, please enlighten me about what is wrong in my code or better, which is best way to do this. Regards,

Secured annotation is used for authorization purposes, for your needs this more suitable (if you wanna use xml config):
<http>
<intercept-url pattern="/yoururl" access="permitAll" />
</http>

Related

Spring singleton #autowired service and shared state

I have a controller and a filter in which I inject a particular service.
In that service I have a Hashmap where I try to store certain information. The issue that I am running into is that although it appears that that a single instance of that service is created and injected into my controller and my filter it seems that there an two instances of the Map. I'm at a loss as to why. No matter how I tried to instantiate the map (or inject it) the behavior is still the same.
It turns out the issue is that two instances of the service are created and injected one in the controller and one in the filter. It's not clear to me why this is happening and how to resolve it.
Following is an extract of the code:
#Controller
public MyController {
#Autowired
private MyService myService;
someEndpoint() {
....
myService.putData(key, value);
.....
}
}
public class MyFilter extends GenericFilterBean {
#Autowired
private MyService myService;
public void doFilter(...) {
//this is where I have a problem.
// the reference myService.myMap seems to be pointing to a different instance
// than the service.myMap in the controller which doesn't make any sense to me
// the filter obviously intercepts all requests so I would expect that after that particular
// endpoint is accessed the data will be there for subsequent requests
myService.getData(..);
}
.....
}
#Service
public class MyService {
private Map <String,String> myMap = new HashMap <String,String> ();
public String getData(String key) {
return myMap.get(key);
}
public void putData(String key, String value){
myMap.put(key,value);
}
}
Here is an extract of the app-config.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:tx="http://www.springframework.org/schema/tx"
xmlns:security="http://www.springframework.org/schema/security"
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.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<context:component-scan base-package="com.mycompany.myPackage"/>
<context:annotation-config />
.......
<security:http
........
........
<security:custom-filter ref="myFilter" position="FORM_LOGIN_FILTER" />
....
...........
<bean class="com.mycompany.filters.MyFilter" id="myFilter"/>
and the web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Dispatcher Servlet</servlet-name>
<url-pattern>/webservice/*</url-pattern>
</servlet-mapping>
<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>
Any help is greatly appreciated.
In your web.xml you set:
<servlet>
<servlet-name>Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
which creates one web application context initiated by your servlet.
Then you also have:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
which also creates a parent root web application context, in your case a duplicate one.
This scheme, properly used, favors the cases where you may have more servlets, each one defining its own isolated context but inherit bean definitions from a common root context (services, datasources etc). It also gives a good practice roadmap for creating layered contexts, i.e. prevent your service beans to have dependencies on your mvc layer.
Unless you have more than one configuration files, you should assign an empty value to the contextConfigLocation of your servlet configuration as :
<servlet>
<servlet-name>Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Be careful. Do not omit the parameter. Spring will infer some default configuration file name based on your servlet and will complain if it does not exist.
GenericFilterBean is not in an application context. I would expect java.lang.InstantiationException in the code above. You can get your bean via the filter's ServletContext. Any other instantiation trick will cause map duplication.
Are you sure that there is only one instance of MyService class created? The simplest way to check this is to provide default constructor implementation and print some text in it. Please check this and let me know, because I have some suspicions.
If this is true, and there are two instances of MyService class, there is possibility that when you put some data to map, you use instance A of MyService and when you get data from the map you use instance B of MyService... I will wait for your response to continue/stop this thinking.

Cann't call authenticate method of spring security for login REST service

I want to check username and password in authenticationProvider but dont know why i cann't get call to authenticate method. I am implementing REST login service through spring security. my web.xml is
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/applicationContext.xml
WEB-INF/spring-security.xml</param-value>
</context-param>
<!-- Spring Security Filter -->
<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>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet
</servlet-class>
<init-param>
<param-name>
com.sun.jersey.config.property.packages
</param-name>
<param-value>com.zymr.ojo.action</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>
<welcome-file-list>
<welcome-file>jsp/login.jsp</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
and spring-security.xml is as below...
<context:component-scan base-package="com.zymr.ojo" />
<security:global-method-security pre-post-annotations="enabled" secured- annotations="enabled" />
<!-- Security configuration for REST services. -->
<security:http use-expressions="true" entry-point-ref="restEntryPoint" create- session="always">
<!-- <security:http-basic/> -->
<security:intercept-url pattern="/rest/question/**" access="isAuthenticated()"/>
<!-- <security:intercept-url pattern="/rest/login/userLogin" access="permitAll"/> -->
<security:form-login login-processing-url="/rest/login/userLogin" username-parameter="admin" password-parameter="password"/>
</security:http>
<!-- Authentication manager. -->
<security:authentication-manager >
<security:authentication-provider user-service-ref="authProvider" >
</security:authentication-provider>
</security:authentication-manager>
<!-- Entry point for REST service. -->
<bean id="restEntryPoint" class="com.zymr.ojo.security.RestAuthenticationEntryPoint" />
<!-- A handler for successful REST service authentication. -->
<bean id="authSuccessHandler" class="com.zymr.ojo.security.RestAuthenticationSuccessHandler"></bean>
<bean id="authProvider" class="com.zymr.ojo.security.RestAuthenticationProvider" ></bean>
and RESTAuthenticationProvider class is
#Component public class
RestAuthenticationProvider implements AuthenticationProvider,
UserDetailsService{
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
System.out.println("in authProvider");
String username = authentication.getName();
String password = (String) authentication.getCredentials();
if(username != null && password != null){
System.out.println(username+ " "+ password);
}
return null;
}
public boolean supports(Class<?> authentication) {
// TODO Auto-generated method stub
return false;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
System.out.println(username);
return null;
}
}
The problem is with the supports(..) method that always returns false. Your entry point should provide the user a way to populate an instance of Authentication and this will be given to the authenticate method if the supports method returns true.
Please refer to the following link. This link specified adding UsernamePasswordAuthenticationFilter. I tried this but it is not calling the login.html form.
http://www.networkedassets.com/configuring-spring-security-for-a-restful-web-services/

#WebFilter vs. <Filter> in web.xml

I wonder how I can achieve a behaviour like using this #WebFilter annotation
#WebFilter(
urlPatterns = "/*",
filterName = "AuthenticationFilter",
description = "Filter all URLs"
,
initParams = {
#WebInitParam(name = "unprotectedUrls", value = "/,/login,/authentication,/notification,/sdr")})
public class AuthenticationFilter implements Filter {
...}
(which works well, meaning that I don't have to be logged in only for the listed paths but for all other paths I have to be)...
by using a <filter> element in web.xml.
Using this filter in web.xml:
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>com.foo.bar.helper.AuthenticationFilter</filter-class>
<init-param>
<param-name>unprotectedUrls</param-name>
<param-value>/,/login,/authentication,/notification,/sdr</param-value>
</init-param>
</filter>
it is not recognized which means that I don't have to be logged in for all paths/URLs. No filtering takes place.
My intention is to make the init-params configurable so that I don't have to edit the code/class whenever a further URL shall be included or so.
You only have defined the filter, you have not mapped the filter. NExt to the filter element you also need a filter-mapping element to map the filter to urls, this is basically the replacement for the urlPatterns attribute of the #WebFilter
<filter-mapping>
<filter-name> AuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

How can I let spring+cometd support webscoket?

I use chrome, it always return:
WebSocket connection to 'ws://192.168.1.228:9090/achat/cometd' failed: Unexpected response code: 400
After this error, it use long-polling transport, and success to connect to the server.
How can I let WebSocket work? Is it caused by using SpringFramework 3.1? Thanks.
Web.xml
<!-- Filter to support cross domain requests -->
<filter>
<filter-name>cross-origin</filter-name>
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
<!--async-supported>true</async-supported-->
</filter>
<filter-mapping>
<filter-name>cross-origin</filter-name>
<url-pattern>/cometd/*</url-pattern>
</filter-mapping>
<!-- Cometd Servlet -->
<servlet>
<servlet-name>cometd</servlet-name>
<servlet-class>org.cometd.annotation.AnnotationCometdServlet</servlet-class>
<!--async-supported>true</async-supported-->
<init-param>
<param-name>timeout</param-name>
<param-value>20000</param-value>
</init-param>
<init-param>
<param-name>interval</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>maxInterval</param-name>
<param-value>10000</param-value>
</init-param>
<init-param>
<param-name>maxLazyTimeout</param-name>
<param-value>5000</param-value>
</init-param>
<init-param>
<param-name>long-polling.multiSessionInterval</param-name>
<param-value>2000</param-value>
</init-param>
<init-param>
<param-name>logLevel</param-name>
<param-value>100</param-value>
</init-param>
<init-param>
<param-name>transports</param-name>
<param-value>org.cometd.websocket.server.WebSocketTransport</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cometd</servlet-name>
<url-pattern>/cometd/*</url-pattern>
</servlet-mapping>
Servlet
#Named
#Singleton
#Service
public class ChatService {
#Inject
private BayeuxServer bayeux;
#Session
private ServerSession serverSession;
#Listener("/chat")
public void processHello(ServerSession remote, ServerMessage.Mutable message)
{
Map<String, Object> input = message.getDataAsMap();
String room = input.get("room").toString();
String content = input.get("content").toString();
Map<String, Object> output = input;
bayeux.getChannel("/room/" + room).publish(remote, output, null);
}
}
Follow the CometD Reference Manual, specifically the section that explains how to integrate CometD with Spring.
There you can find an example that uses Spring's XML configuration to setup WebSocket, and an example of how to configure Spring via annotations.
Your question lacks the Spring configuration file and part of the web.xml file so it is difficult to help you in a more detailed way.
Just remember that if you configure CometD in Spring, then you must remove all the CometD configuration from web.xml. You must have only one place where you configure things: either web.xml or Spring's XML/annotations.

Creating secure pages in Spring

I am creating a website using Spring and want all pages under the folder "/admin" to be secure. However don't really know where to start and only have one complicated example to go on.
At work, we store the details in a database but I was hoping it could be more simple than that, maybe stored in context.xml or something? I am confronted with this page:
web.xml:
<security-constraint>
<display-name>admin pages</display-name>
<web-resource-collection>
<web-resource-name>Administration Pages</web-resource-name>
<description/>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description/>
<role-name>userAdmin</role-name>
</auth-constraint>
<!-- <user-data-constraint>
<description/>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>-->
</security-constraint>
and in tomcat-users.xml I have the following password information:
<user password="password" roles="tomcat,role1,manager-script,manager-gui,admin,manager" username="user"/>
But when I try and access the page /admin/adminindex.htm, I get a forbidden error:
Access to the specified resource (Access to the requested resource has been denied) has been forbidden.
Ideally I would like to store user details in the database but can't progress with either at the moment.
I would look into Spring Security, which offers a plethora of options for securing websites (including DB-backed or JNDI-backed security). The tutorial may prove a good starting point.
This is how I secure applications using Spring Security, here is the web.xml
<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>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-servlet.xml
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/myapp/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
spring-security.xml
<security:http auto-config="true" use-expressions="true" access-denied-page="/" create-session="never" disable-url-rewriting="true">
<security:intercept-url pattern="/myapp/auth/login" access="permitAll" />
<security:intercept-url pattern="/myapp/main/**" access="hasRole('ROLE_USER')" />
<security:form-login login-page="/" authentication-failure-url="/myapp/auth/login?error=true" default-target-url="/myapp/main/default"/>
<security:logout invalidate-session="true" logout-success-url="/myapp/auth/login" logout-url="/myapp/auth/logout" />
</security:http>
In order to authenticate using a Database you can use an Authentication Manager like this in spring-security.xml
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<security:password-encoder ref="passwordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
Where "userService" is a service you define that has access to the Database, your service must implement org.springframework.security.core.userdetails.UserDetailsService and write the method
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException, DataAccessException {
UserDetails user = null;
try {
// Replace loadUserFromDB with your Data access method to pull the user and encrypted password from the database
Users u = loadUserFromDB(userName);
if(u != null)
user = new User(u.getEmail(), u.getPassword().toLowerCase(), true, true, true, true, getAuthorities(0));
} catch (Exception e) {
e.printStackTrace();
}
return user;
}
Spring security will use this method to secure your pages. Make sure to include this method:
public Collection<GrantedAuthority> getAuthorities(Integer access) {
// Create a list of grants for this user
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(1);
authList.add(new GrantedAuthorityImpl("ROLE_USER"));
authList.add(new GrantedAuthorityImpl("ROLE_ANONYMOUS"));
return authList;
}
I would start with this: http://docs.spring.io/autorepo/docs/spring-security/3.0.x/reference/springsecurity.html You can also check out this project that already has the basic code to start using Spring Security
https://github.com/pgardunoc/spring-security

Resources