How to secure a REST Web Service using Spring Security - spring

I am trying to secure my REST web services at the url /dispatcher/rest/**
My current design works fine if accessing the web service through browser - when I try to go to the REST url it redirects me to a login page to enter credentials, then redirects me to the web service data once logged in.
The problem is that when I try to access the web service through java code using RestTemplate, my code breaks. This occurs even if the user has already logged in and authenticated.
My spring-security.xml config file:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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.2.xsd">
<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/dispatcher/admin**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/dispatcher/rest/**" access="hasRole('ROLE_ADMIN')"/>
<!-- access denied page -->
<access-denied-handler error-page="/dispatcher/403" />
<form-login
login-page="/dispatcher/login"
default-target-url="/dispatcher/admin"
login-processing-url="/dispatcher/login_process"
authentication-failure-url="/dispatcher/login?error"
username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/dispatcher/login?logout" logout-url = "/dispatcher/logout"/>
<!-- enable csrf protection -->
<csrf />
</http>
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService" >
</authentication-provider>
</authentication-manager>
<beans:bean id="myUserDetailsService" class="com.shopping.services.MyUserDetailsService" />
</beans:beans>
Any help would be greatly appreciated!

The solution I found to access the web service is the following:
String username = ((UserDetails) principal).getUsername();
String password = ((UserDetails) principal).getPassword();
HttpClient client = new HttpClient();
client.getParams().setAuthenticationPreemptive(true);
Credentials defaultcreds = new UsernamePasswordCredentials(username, password);
restTemplate.setRequestFactory(new CommonsClientHttpRequestFactory(client));
client.getState().setCredentials(AuthScope.ANY, defaultcreds);
User x = restTemplate.getForObject("http://localhost:8080/Online_Shopping/dispatcher/rest/hello",User.class);

Related

Multiple spring security configuration not working

In my application i want to have separate spring security implementation based on url patterns.
Eg. /rest/ ** will have its own authentication provider(basic auth) and
/web/ ** will have its own authentication provider(form login).
please find below configuration i have done
<?xml version="1.0"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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.xsd">
<!-- config for rest services using basic auth-->
<http pattern="/rest/**">
<intercept-url pattern="/MyAppRestServices" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<http-basic />
</http>
<!-- AUTHENTICATION MANAGER FOR CUSTOM AUTHENTICATION PROVIDER -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
<!-- config for web using form login-->
<http pattern="/web/**">
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
<form-login/>
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="nimda" authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
In above config first config is working fine ie restservice with basic auth but web with form login config is not working. its not even intercepting the url ?
Please let me know whats wrong with above config ?
Kindly refer below working configuration for web authentication::
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http pattern="/css/**" security="none" />
<http pattern="/images/**" security="none" />
<http pattern="/js/**" security="none" />
<http auto-config="false" authentication-manager-ref="dev" use-expressions="true" disable-url-rewriting="true">
<intercept-url pattern="/admin/login" access="permitAll" />
<intercept-url pattern="/admin/*" access="isAuthenticated()" />
<form-login
login-page="/admin/login"
default-target-url="/admin/workbench"
username-parameter="username"
password-parameter="password"
authentication-failure-url="/admin/login"
/>
<logout logout-success-url="/admin/login" logout-url="/j_spring_security_logout" invalidate-session="true" delete-cookies="JSESSIONID" />
</http>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
<!-- STATIC USER -->
<authentication-manager id="dev" alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="abc" password="pwd" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>

Spring security custom session timeout url

Currently I'm working on spring security app, where I need user to be redirected to a custom lock page when session is expired which only contains a password field. (username might be placed in a hidden field inside the login form.) I need to pass username extracted from UserDetails instance and redirect to this custom URL once login session timed out.
I tried to use concurrency-control -> expired-url but it did not give me successful result.
I will be very helpful if you could give some guidence to achieve this.
Is this possible to achieve since I will need to have customized behavior on authentication-failure-url too? (when a given password is incorrect).
Update 1:
My current configuration:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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-4.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd">
<http auto-config="true" create-session="always" use-expressions="true" entry-point-ref="authEntryPoint" >
<form-login
login-page="/login"
default-target-url="/home"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password"
always-use-default-target="true"/>
<logout invalidate-session="true" logout-success-url="/login" delete-cookies="JSESSIONID"/>
<session-management session-fixation-protection="newSession" invalid-session-url="/"
session-authentication-error-url="/login">
<concurrency-control session-registry-alias="sessionRegistry" max-sessions="10"
expired-url="/lock-screen" error-if-maximum-exceeded="true"/>
</session-management>
<intercept-url pattern="/" access="hasRole('ROLE_LOGIN')"/>
<intercept-url pattern="/profile" access="hasRole('ROLE_LOGIN')"/>
<intercept-url pattern="/home" access="hasRole('ROLE_LOGIN')"/>
<access-denied-handler error-page="/403"/>
</http>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder ref="passwordEncoder"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="10"/>
</beans:bean>
<beans:bean id="authEntryPoint" class="com.myapp.admin.security.web.authentication.AjaxSupportedLoginUrlAuthenticationEntryPoint"
scope="singleton">
<beans:constructor-arg name="loginFormUrl" value="/login"/>
</beans:bean>
I'm expecting something like this:
user Login screen:
https://i.stack.imgur.com/vTPkF.png
Lock screen after logged in user being inactive for while:
https://i.stack.imgur.com/DY9kG.png

Spring Security sec:authorize throw exception

I want to add a field to my jsp which will be shown only to admin. For this purpose I use tag sec:authorize access="hasRole('Admin')". But when I add it, application throws exception: http://pastebin.com/TcN0k0K0
I use spring 4.1.7.RELEASE, spring-security version 4.0.3.RELEASE. In pom.xml I've added spring-security-taglibs v.4.0.3
here is my jsp code:
<%# taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<body>
<sec:authorize access="hasRole('Admin')">
<p>Must have ROLE_Admin to see this</p>
</sec:authorize>
<form name='registerForm' method='POST' action="/admin/createuser">
...
in database role stored as ROLE_Admin, ROLE_User
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<http use-expressions="true" >
<csrf disabled="true"/>
<intercept-url pattern="/admin" access="hasAnyRole('Admin', 'User')" />
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/logout" access="permitAll" />
<access-denied-handler error-page="/403" />
<form-login login-page='/login' login-processing-url="/login" authentication-failure-url="/403"
default-target-url="/admin"
username-parameter="login" password-parameter="password" />
<logout logout-url="/logout" logout-success-url="/logoutSuccessful" delete-cookies="JSESSIONID" invalidate-session="true" />
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="myDataSource"
users-by-username-query= "select login, password, 'true' from employee where login=?"
authorities-by-username-query= "select login, role from employee where login =? " />
</authentication-provider>
</authentication-manager>
<beans:import resource="data-source-cfg.xml"/>
</beans:beans>
How to fix this problem?
take a look at this post, the problem you are getting is about the spring version. You have two options:
1 - To keep using spring security 4.0.3 you must upgrade Spring version for 4.2.x.
2 - To keep using your current spring version you must downgrade to the Spring security 4.0.2
Best Regards

Spring security #PreAuthorize not working with embedded jetty

My application is having embedded jetty integrated with spring security for authentication and authorization. Authentication and authorization works fine for REST api calls to the application, but method authorization using #PreAuthorize is not working.
My spring-security.xml file has <global-method-security pre-post-annotations="enabled" />
After some search I found that it is recommended to move global-method-security to servlet-context.xml under application context, but I cannot do it because we are not using "org.springframework.web.servlet.DispatcherServlet". We are already using "org.glassfish.jersey.servlet.ServletContainer" as dispatcher.
spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
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.1.xsd">
<global-method-security pre-post-annotations="enabled" />
<http realm="Protected API" use-expressions="true"
create-session="stateless"
entry-point-ref="unauthorizedEntryPoint"
authentication-manager-ref="edbAuthenticationManager">
<custom-filter ref="edbAuthenticationFilter" position="FORM_LOGIN_FILTER"/>
<intercept-url pattern="/api/*/login" access="authenticated"/>
<intercept-url pattern="/api/*/clusters/*/datasupport/**" access="hasRole('data-scientist')"/>
<intercept-url pattern="/api/*/clusters/*/job/**" access="hasRole('data-scientist')"/>
<intercept-url pattern="/api/*/clusters/*/task/**" access="hasRole('data-scientist')"/>
<intercept-url pattern="/api/*/clusters/**" access="hasRole('cluster-admin')"/>
<http-basic />
</http>
<beans:bean id="unauthorizedEntryPoint" class="edbServer.server.security.EntryPoint"/>
<beans:bean id="userDetailService" class="edbServer.server.security.UserDetailsServiceImp"/>
<beans:bean name="bcryptEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<authentication-manager id="edbAuthenticationManager">
<authentication-provider user-service-ref="userDetailService">
<password-encoder ref="bcryptEncoder"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="tokenManager" class="edbServer.server.security.TokenManager"/>
<beans:bean id="authenticationService" class="edbServer.server.security.EDBAuthenticationService"
c:authenticationManager-ref="edbAuthenticationManager" c:tokenManager-ref="tokenManager" c:inactivityTimeoutSeconds="1800" />
<beans:bean id="edbAuthenticationFilter" class="edbServer.server.security.EDBTokenAuthenticationFilter"
c:authenticationService-ref="authenticationService" c:loginLink="/api/v1/login" c:logoutLink="/api/v1/logout"/>
</beans:beans>
Relevant code to initialize jetty server
String SPRING_CONTEXT_LOCATION = "classpath:/webapp/WEB-INF/spring-security.xml";
ClassPathXmlApplicationContext parentSpringAppContext = new ClassPathXmlApplicationContext(contextLocations);
parentSpringAppContext.refresh();
ClassPathXmlApplicationContext springAppContext = new ClassPathXmlApplicationContext(
parentSpringAppContext);
springAppContext.refresh();
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SECURITY
| ServletContextHandler.SESSIONS);
contextHandler.setContextPath(CONTEXT_PATH);
GenericWebApplicationContext springWebAppContext = new GenericWebApplicationContext();
springWebAppContext.setServletContext(contextHandler.getServletContext());
springWebAppContext.setParent(springAppContext);
contextHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
springWebAppContext);
DelegatingFilterProxy springSecurityFilter = new DelegatingFilterProxy();
springSecurityFilter.setTargetBeanName("springSecurityFilterChain");
contextHandler.addFilter(new FilterHolder(springSecurityFilter), "/api/*", EnumSet.of(DispatcherType.REQUEST));
ServletHolder sh = new ServletHolder(ServletContainer.class);
sh.setInitParameter("org.glassfish.jersey.server.ResourceConfig", "JacksonFeature.class");
sh.setInitParameter("jersey.config.server.provider.packages", "edbServer.server.api.services;");
sh.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
contextHandler.addServlet(sh, "/api/v1/*");
HandlerList handlerList = new HandlerList();
handlerList.addHandler(contextHandler);
server.setHandler(handlerList);
springAppContext.start();
server.start();
how can I set global-method-security in app context here?
Thanks,
Saurabh

Spring security concurrent session is not working as desired

Instead of restricting one session per user,it is restricting one session for
whole application.
So if one user is logged in noone can login .
Here is my configuration
<session-management invalid-session-url="/login">
<concurrency-control error-if-maximum-exceeded="true" max-sessions="1" />
</session-management>
And i even added listener in web.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- HTTP security configurations -->
<http auto-config="true" use-expressions="true">
<form-login login-processing-url="/resources/j_spring_security_check"
login-page="/login" default-target-url="/index"
authentication-success-handler-ref="myAuthenticationSuccessHandler"
authentication-failure-url="/login?login_error=t" />
<logout invalidate-session="true"
logout-url="/resources/j_spring_security_logout" success-handler-ref="myLogoutSuccessHandler"/>
<!-- Configure these elements to secure URIs in your application -->
<intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/member/**" access="isAuthenticated()" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/**" access="permitAll" />
<session-management invalid-session-url="/login">
<concurrency-control error-if-maximum-exceeded="true"
max-sessions="1" />
</session-management>
</http>
<!-- Configure Authentication mechanism -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customDaoAuthenticationProvider">
</authentication-provider>
</authentication-manager>
<beans:bean id="myAuthenticationSuccessHandler" class="com.test.connect.web.login.MyAuthenticationSuccessHandler"/>
<beans:bean id="myLogoutSuccessHandler" class="com.test.connect.web.login.MyLogoutSuccessHandler"/>
</beans:beans>
Based upon the configuration you provided, which includes a custom AuthenticationProvider, and the problem you are having I would guess that you are returning a custom UserDetails implementation that does not properly implement the equals and hashCode methods.
Please ensure that you have properly implemented equals and hashCode on any custom UserDetails implementation as these methods are used to look up if a user contains active sessions.
Just want to highlight here, make sure the equals and hashCode methods return is true. if the methods is not returning true it will not kill or terminate the existing session.

Resources