I have a web application which uses spring security for session based logins using username and password authentication with the following security application context xml.
<global-method-security pre-post-annotations="enabled" />
<http pattern="/css/**" security="none" />
<http pattern="/files/**" security="none" />
<http auto-config='true' entry-point-ref="authenticationEntryPoint" access-decision-manager-ref="accessDecisionManager">
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" method="OPTIONS" />
<intercept-url pattern="/login/*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/**" access="REGISTERED" />
<form-login login-page="/login" login-processing-url="/login_security_check" authentication-failure-handler-ref="xxxAuthenticationFailureHandler" authentication-success-handler-ref="xxxAuthenticationSuccessHandler" />
<logout invalidate-session="true" logout-url="/data/logout" success-handler-ref="xxxLogoutSuccessHandler" />
<remember-me key="xxxRem" />
</http>
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="decisionVoters">
<beans:list>
<beans:ref bean="roleVoter" />
<beans:ref bean="authenticatedVoter" />
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter">
<beans:property name="rolePrefix" value="" />
</beans:bean>
<beans:bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter">
</beans:bean>
<beans:bean id="userDetailsService" class="com.xxx.web.security.XXXUserDetailsService">
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref='userDetailsService'>
<password-encoder hash="md5">
<salt-source user-property="username" />
</password-encoder>
</authentication-provider>
</authentication-manager>
<beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg value="/login" />
</beans:bean>
<beans:bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
<beans:property name="defaultEntryPoint" ref="loginUrlAuthenticationEntryPoint" />
</beans:bean>
Now I want to expose my web services for a mobile app, so I am looking to implement OAuth2. I've read the examples provided on github.
I was wondering how these two security flows can co-exist as the intercept url pattern will be same for both the flows?
You would need to change the access="" rules for the resources that are shared between the UI and the OAuth resources. It's not very common for those two to really cross over very much, but I guess for simple apps it might be possible through content negotiation. It's probably easiest to use the SpEL support in XML (or switch to Java config). Example:
<intercept-url pattern="/** access="isFullyAuthenticated() or #oauth2.hasScope('read')"/>
For an alternative approach you could create aliases for your endpoints and protect them with separate filter chains, one for the token and one for cookie-based authentication. Example:
#RequestMapping({ "/user", "/api/user" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
where "/user" is protected as a normal resource (i.e. with WebSecurityConfigurerAdapter in Java config), and "/api/user" is separately configured (i.e. with a ResourceServerConfigurerAdapter in Java config).
Related
I have setup a security context meant for REST. The configuration is as
<!-- authentication manager and password hashing -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="daoAuthenticationProvider" />
</authentication-manager>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService" />
<beans:property name="passwordEncoder" ref="passwordEncoder" />
</beans:bean>
<beans:bean id="userDetailsService" name="userAuthenticationProvider"
class="com.myapp.auth.AuthenticationUserDetailsGetter" />
<beans:bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
</beans:bean>
<global-method-security pre-post-annotations="enabled" />
<!-- web services -->
<http use-expressions="true" pattern="/rest/**"
disable-url-rewriting="true" entry-point-ref="restAuthenticationEntryPoint">
<custom-filter ref="restProcessingFilter" position="FORM_LOGIN_FILTER" />
<intercept-url pattern="/rest/login" access="permitAll"/>
<intercept-url pattern="/rest/**" access="isAuthenticated()" />
<logout delete-cookies="JSESSIONID" />
</http>
<beans:bean id="restProcessingFilter" class="com.myapp.auth.RestUsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="filterProcessesUrl" value="/rest/login" />
</beans:bean>
And I overrided the UsernamePasswordAuthenticationFilter as
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = null;
String username = request.getParameter("j_username");
String password = request.getParameter("j_password");
boolean valid = authService.authenticate(username, password);
if (valid) {
User user = updateLocalUserInfo(username);
authentication = new UsernamePasswordAuthenticationToken(user,
null, AuthorityUtils.createAuthorityList("USER"));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
return authentication;
}
The above authentication is working fine when I tried it with
RestClient restClient = new RestClient();
String result = restClient.login("hq", "a1234567"); // RestTemplate.postForObject
The only thing left is the result from the authentication post (atm, result is null). How can I configure my security configuration in order to retrieve some result ? A flag or session ID will suffice.
I think best bet here would be AuthenticationSuccessHandler.
As this will only kick in if the authentication was successful.
You can generate some sort of UUID and set that in your response directly. I have used very similar approach for ReST Auth and have not hit any problems yet.
For detailed implementation guide please refer : https://stackoverflow.com/a/23930186/876142
Update for comment #1 :
You can get response just like any normal ReST request.
This is how I am sending back my Token as JSON
String tokenJsonResponse = new ObjectMapper().writeValueAsString(authResponse);
httpResponse.addHeader("Content-Type", "application/json");
httpResponse.getWriter().print(tokenJsonResponse);
Assuming you know how to use RestTemplate, rest is trivial.
I'm using Spring Security 3.2.5 in a GWT application. I needed fine-grained control over security so I used the following configuration instead of element:
<beans:bean id="springSecurityFilterChain"
class="org.springframework.security.web.FilterChainProxy">
<beans:constructor-arg>
<beans:list>
<filter-chain pattern="/css/**" filters="none" />
<filter-chain pattern="/image/**" filters="none" />
<filter-chain pattern="/index.jsp" filters="none" />
<filter-chain pattern="/**/logout" filters="logoutFilter" />
<filter-chain pattern="/**"
filters="securityContextPersistenceFilterWithASCTrue, concurrentSessionFilter, usernamePasswordAuthenticationFilter, exceptionTranslationFilter, filterSecurityInterceptor" />
</beans:list>
</beans:constructor-arg>
</beans:bean>
I've omitted particular filter implementations.
I need to force most of filter-chain's above to use https like when using the tag as in the following example:
<security:intercept-url pattern="/reports" access="ROLE_ADMIN" requires-channel="https"/>
How can I achieve this?
EDIT 1: adding a ChannelProcessingFilter
Following #luke answer I modified my code so a channel filter is in the first position of the filter chain:
<filter-chain pattern="/**"
filters="channelProcessingFilter, securityContextPersistenceFilterWithASCTrue, ..." />
I also added the following beans configuration:
<!-- Ensure https channel -->
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<beans:property name="securityMetadataSource">
<filter-security-metadata-source>
<intercept-url pattern="/**" access="ROLE_USER" />
</filter-security-metadata-source>
</beans:property>
</beans:bean>
<beans:bean id="channelProcessingFilter" class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
<beans:property name="channelDecisionManager" ref="channelDecisionManager"/>
<beans:property name="securityMetadataSource">
<filter-security-metadata-source request-matcher="ant">
<intercept-url pattern="/**" access="REQUIRES_SECURE_CHANNEL"/>
</filter-security-metadata-source>
</beans:property>
</beans:bean>
<beans:bean id="channelDecisionManager" class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
<beans:property name="channelProcessors">
<beans:list>
<beans:ref bean="secureChannelProcessor"/>
<beans:ref bean="insecureChannelProcessor"/>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor" />
<beans:bean id="insecureChannelProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor" />
Now the problem is I'm getting an infinite loop after submitting my login form via http. Of course it is the case I want to avoid but an infinite loop is not right. This is the relevant log:
DEBUG o.s.s.w.FilterChainProxy 337 - /j_spring_security_check at
position 1 of 6 in additional filter chain; firing Filter:
'ChannelProcessingFilter'
DEBUG o.s.s.w.a.c.ChannelProcessingFilter 134
- Request: FilterInvocation: URL: /j_spring_security_check; ConfigAttributes: [REQUIRES_SECURE_CHANNEL] 2014-10-30 19:47:10,565
DEBUG o.s.s.w.a.c.RetryWithHttpsEntryPoint 55 - Redirecting to:
/j_spring_security_check 2014-10-30 19:47:10,567 DEBUG
o.s.s.w.DefaultRedirectStrategy 36 - Redirecting to
'/j_spring_security_check'
Any ideas please?
You need a ChannelProcesingFilter.
It's best if you just require HTTPS to access all of your site. HTTPS is only really secure if you use it from the start. Ideally also you want to use HSTS to communicate that to clients.
I have successfully setup LDAPS container-based authentication, and am now trying to get it working with spring security since I will also need to perform lookups/queries.
In WAS I have all the endpoints using the correct keystore (except for WC_DefaulHost). Additionally, I also setup Dynamic endpoint config for ldaps,host,port.
When i try to log in, I'm just getting "spring_security_login?login_error" and no system.out exceptions.
Am I missing something? Aren't endpoint configurations enough? Any way I can get more info to troubleshoot?
<?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.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<authentication-manager>
<authentication-provider ref="ldapAuthProvider" />
</authentication-manager>
<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<!-- AD authenticator -->
<beans:constructor-arg value="ldaps://host:port/DC=" />
<beans:property name="userDn" value="CN=,OU=,DC=" />
<beans:property name="password" value="" />
</beans:bean>
<beans:bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean id="wimLdapAuthenticator"
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userSearch">
<beans:bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<beans:constructor-arg index="0" value="" />
<beans:constructor-arg index="1" value="CN={0}" />
<beans:constructor-arg index="2" ref="contextSource" />
</beans:bean>
</beans:property>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<http auto-config="true" pattern="/**">
<!-- Security zones -->
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<intercept-url pattern="/spring_security_login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
</beans:beans>
It's working now.. seems like it wasn't an SSL problem... I switched the order of the intercept-url so that /** is the last one and added a custom login form..
<form-login login-page="/login" default-target-url="/viewAllTeams" authentication-failure-url="/loginfailed" />
<logout logout-success-url="/logout" />
<form-login default-target-url="/viewAllTeams"/>
<intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/loginfailed" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
I also found that you can show exceptions using the following:
<div class="errorblock">
Your login attempt was not successful, try again.<br /> Caused :
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>
I try to implement my own example based on official tutorial Sparklr2/Tonr2. Everything looks good but when I remove from web.xml in my Tonr2 implementation, spring security filter I have exception:
No redirect URI has been established for the current request
I can't understand what URL should I use. Here is my code, for client implementation:
<!--apply the oauth client context -->
<oauth:client id="oauth2ClientFilter" />
<!--define an oauth 2 resource for sparklr -->
<oauth:resource id="provider" type="authorization_code" client-id="client" client-secret="secret"
access-token-uri="http://localhost:8080/provider/oauth/token" user-authorization-uri="http://localhost:8080/provider/oauth/authorize" scope="read,write" />
<beans:bean id="clientController" class="com.aouth.client.ClientController">
<beans:property name="trustedClientRestTemplate">
<oauth:rest-template resource="provider" />
</beans:property>
</beans:bean>
And for provider:
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic />
</http>
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<!-- The OAuth2 protected resources are separated out into their own block so we can deal with authorization and error handling
separately. This isn't mandatory, but it makes it easier to control the behaviour. -->
<http pattern="/secured" create-session="never" access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/secured" access="ROLE_USER,SCOPE_READ" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<http-basic />
</http>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased" xmlns="http://www.springframework.org/schema/beans">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<oauth:resource-server id="resourceServerFilter" resource-id="resource" token-services-ref="tokenServices" />
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails"/>
</bean>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
<http auto-config="true" xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/test" access="ROLE_USER" />
<intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider>
<user-service>
<user name="pr" password="pr" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" >
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password />
</oauth:authorization-server>
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="client" resource-ids="resource" authorized-grant-types="authorization_code, implicit"
authorities="ROLE_CLIENT" scope="read,write" secret="secret" />
</oauth:client-details-service>
I just want my client to work without spring security. And when I need my protected resource I want to login only on provider side.
You 2nd XML that you pasted here is the spring's XML for the oauth-provider and the protected-resource, which in your case run in the same webapp. (you can separate them, of course, if you wish).
The client (the 1st pasted-XML) is a different story. If I understand you correctly, you want your client to run without Spring's help (to be a regular webapp, and not spring-security-oauth-client webapp).
You have to understand how oAuth works: the client tries to get to a protected resource; if it does not have the access-token, it is being redirected to the oAuth-provider (that shows the login page and supplies the token). By the standard, the request for the access-token MUST contain a "redirect-uri" param, so after a successful login, the oAuth-provider knows where to redirect the client to. The oAuth client does it for you, and if you delete the "oauth client" from your web.xml, you now have to implement this by yourself.
Thanks for your answer. But I still don't understand how spring
security influences my oAuth client. And can I use for client side
spring-oauth (spring-mvc) without spring-security?
When you write this line in your XML:
< oauth:client id="oauth2ClientFilter" />
it means that you use spring-security-oauth, which is a package dedicated for oauth, built on spring-security. If you dig in, it puts a special filter (OAuth2ClientContextFilter) in the chain that handles the oAuth stuff, that are relevant for the client. One of them is sending the request with all the params ("redirect-uri" is one of them).
If you decide NOT to use spring-security-oauth, well - you will have to implement this logic by yourself...
Hope that helps!
I would like to base my Spring Security configuration depending on the user's context path. If the user goes against a url with http://path1/resource1 I would like to direct them to a specific authentication provider. If they come in on http://path2/resource2 I would like to direct them to a different authentication provider. These url paths are REST based web services calls so that's why they're stateless and not coming from a form. Currently, all authentication providers get executed. What is the best approach for this situation? I'm using spring-security 3.1.0.M1.
<http pattern="/path1/**" create-session="stateless">
<intercept-url pattern="/**" access="ROLE_USER,ROLE_VAR,ROLE_ADMIN" />
<http-basic />
</http>
<http pattern="/path2/**" create-session="stateless">
<intercept-url pattern="/**" access="ROLE_USER,ROLE_VAR,ROLE_ADMIN" />
<http-basic />
</http>
You can define an authentication-manager reference in each http block:
<http pattern="/api/**" authentication-manager-ref="apiAccess">
...
</http>
<http auto-config = "true" authentication-manager-ref="webAccess">
...
</http>
<!-- Web authentication manager -->
<authentication-manager id="webAccess">
<authentication-provider
user-service-ref="userService">
</authentication-provider>
</authentication-manager>
<!-- API authentication manager -->
<authentication-manager id="apiAccess">
<authentication-provider
user-service-ref="developerService">
</authentication-provider>
</authentication-manager>
This feature has been added in Spring Security 3.1.
This works for me:
<security:authentication-manager alias="basicAuthenticationManager">
<security:authentication-provider user-service-ref="accountService">
<security:password-encoder hash="sha"/>
</security:authentication-provider>
<security:authentication-provider user-service-ref="accountService"/>
</security:authentication-manager>
<bean id="basicProcessingFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<property name="authenticationManager">
<ref bean="basicAuthenticationManager" />
</property>
<property name="authenticationEntryPoint">
<ref bean="basicProcessingEntryPoint" />
</property>
</bean>
<bean id="basicProcessingEntryPoint"
class="com.yourpackage.web.util.CustomBasicAuthenticationEntryPoint">
<property name="realmName" value="yourRealm" />
</bean>
<!-- Stateless RESTful service using Basic authentication -->
<security:http pattern="/rest/**" create-session="stateless" entry-point-ref="basicProcessingEntryPoint">
<security:custom-filter ref="basicProcessingFilter" position="BASIC_AUTH_FILTER" />
<security:intercept-url pattern="/rest/new" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:intercept-url pattern="/rest/**" access="ROLE_USER" />
</security:http>
<!-- Additional filter chain for normal users, matching all other requests -->
<security:http use-expressions="true">
<security:intercept-url pattern="/index.jsp" access="permitAll" />
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<security:form-login login-page="/signin"
authentication-failure-url="/signin?signin_error=1"
default-target-url="/"
always-use-default-target="true"/>
<security:logout />
</security:http>
I implemented the authentication entry point because I needed to send some special error codes in certain situations but you don't need to do so.