Spring Security Oauth - Custom UserDetailsService is never getting called - java

spring-security.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:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">
<!-- Default url to get a token from OAuth -->
<http pattern="/api/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<csrf disabled="true"/>
<intercept-url pattern="/api/oauth/token" access="isFullyAuthenticated()"/>
<anonymous enabled="false"/>
<http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>
<!-- Protected sources -->
<http pattern="/api/services/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint" access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security" use-expressions="true">
<anonymous enabled="false"/>
<intercept-url pattern="/api/**" access="hasRole('ROLE_APP')"/>
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager"/>
</bean>
<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.web.access.expression.WebExpressionVoter"/>
</list>
</constructor-arg>
</bean>
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService"/>
</authentication-manager>
<sec:authentication-manager>
<sec:authentication-provider user-service-ref='userDetailsServiceImpl'/>
</sec:authentication-manager>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails"/>
</bean>
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="userDetailsServiceImpl"></authentication-provider>
</authentication-manager>
<!-- Token store -->
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore"/>
<property name="supportRefreshToken" value="true"/>
<property name="accessTokenValiditySeconds" value="120"/>
<property name="clientDetailsService" ref="clientDetails"/>
</bean>
<bean id="oAuth2RequestFactory" class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
<constructor-arg ref="clientDetails"/>
</bean>
<bean id="userApprovalHandler" class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
<property name="tokenStore" ref="tokenStore"/>
<property name="requestFactory" ref="oAuth2RequestFactory"/>
</bean>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test/client"/>
<property name="typeName" value="Basic"/>
</bean>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test/client"/>
<property name="typeName" value="Basic"/>
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler">
<oauth:authorization-code/>
<oauth:implicit/>
<oauth:refresh-token/>
<oauth:client-credentials/>
<oauth:password/>
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" resource-id="test" token-services-ref="tokenServices"/>
<bean id="clientDetails" class="com.xxxxxx.service.ClientService"></bean>
<sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
<sec:expression-handler ref="oauthExpressionHandler"/>
</sec:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler"/>
<oauth:web-expression-handler id="oauthWebExpressionHandler"/>
</beans>
UserServiceImpl
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private OauthRepository oauthRepository;
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = oauthRepository.getByUsername(s);
return user;
}
}
ClientServiceImpl
#Component
public class ClientService implements ClientDetailsService {
#Autowired
private OauthRepository oauthRepository;
#Override
public ClientDetails loadClientByClientId(String s) throws ClientRegistrationException {
BaseClientDetails clientDetails = oauthRepository.getByClientId(s);
return clientDetails;
}
}
I've been searching a lot, but I can't figure out why the UserServiceImpl is not called. I debbuged and always ClientServiceImpl is being called to fetch for client AND users.
After sending a request to /api/oauth/token, with proper parameters (user and client stored in database), I get:
{"error":"unauthorized","error_description":"Incorrect result size: expected 1, actual 0"}
I'm using
- Spring Framework 4.2.5.RELEASE
- Spring Mvc 4.2.5.RELEASE,
- Spring Security 4.1.0.RC1,
- Spring Security Oauth2 2.0.9.RELEASE

I think you are doing good but in the
#Service public class UserDetailsServiceImpl implements UserDetailsService
You should set the user’s password and its role.
Please look code here
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException
{
System.out.println("Getting access details from Database !!");
// Ideally it should be fetched from database and populated instance of
// #org.springframework.security.core.userdetails.User should be returned from this method
UserDetails user = new User(username, "password", true, true, true, true, new GrantedAuthority[]{ new GrantedAuthorityImpl("ROLE_USER") });
return user;
}
UserDetailsService interface is used in order to lookup the username, password and GrantedAuthorities for any given user. This interface provide only one method which implementing class need to implement.

I solved it with the following changes in spring-security.xml:
<!-- Default url to get a token from OAuth -->
<http pattern="/api/oauth/token" create-session="stateless"
authentication-manager-ref="authenticationManager"
:
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="userDetailsServiceImpl"/>
</authentication-manager>
I guess I had two instances fo authenticationMangager. Thanks anyway.

Related

Spring security and oauth2 authentication problem

My problem is if I use org.springframework.security.authentication.ProviderManager for my autheticationManager in applicationContext-security.xml then I unable to authenticate with oauth2. If I switch back to authentication-manager id="authenticationManager" version then it works fine.
I have to keep my REST service and oauth2 security settings in separate files. (Sometimes we don't need for REST service at all).
spring-security 4.2.9.RELEASE
spring-security-oauth2 2.3.4
web.xml snippet:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
/WEB-INF/rest-dispatcher-servlet.xml
/WEB-INF/rest-dispatcher-servlet-security.xml
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
rest-dispatcher-servlet-security
<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:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:secdp="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Definition of the Authentication Service -->
<secdp:http use-expressions="false" 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"/>
<secdp:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
<secdp:csrf disabled="true"/>
</secdp:http>
<!-- Protected resources -->
<secdp:http use-expressions="false" pattern="/ws/api/**"
create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false"/>
<intercept-url pattern="/ws/api/**"
access="ROLE_USER"/>
<custom-filter ref="resourceServerFilter"
before="PRE_AUTH_FILTER"/>
<access-denied-handler
ref="oauthAccessDeniedHandler"/>
<secdp:csrf disabled="true"/>
</secdp:http>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="dstest"/>
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="dstest/client"/>
<property name="typeName" value="Basic"/>
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager"/>
</bean>
<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>
<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>
<!-- Token Store -->
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore"/>
<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="userApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
<property name="tokenStore" ref="tokenStore"/>
<property name="requestFactory" ref="oAuth2RequestFactory"/>
</bean>
<!-- Token management -->
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices"
user-approval-handler-ref="userApprovalHandler" >
<oauth:authorization-code/>
<oauth:implicit/>
<oauth:refresh-token/>
<oauth:client-credentials/>
<oauth:password/>
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter"
resource-id="dstest"
token-services-ref="tokenServices"/>
<!-- Client Definition -->
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="xxxxxxxxx"
authorized-grant-types="password,authorization_code,refresh_token,implicit,redirect"
authorities="ROLE_USER, ROLE_TRUSTED_USER"
redirect-uri="/"
scope="read,write,trust"
access-token-validity="2678400"
refresh-token-validity="15552000" />
</oauth:client-details-service>
<bean class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory" id="oAuth2RequestFactory">
<constructor-arg ref="clientDetails" />
</bean>
</beans>
applicationContext-security.xml snippet
<!-- works -->
<authentication-manager id="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="CustomUserDetailsService">
<password-encoder ref="passwordEncoder"/>
</authentication-provider>
</authentication-manager>
<!-- does not work -->
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<list>
<ref bean="daoAuthenticationProvider"/>
</list>
</constructor-arg>
</bean>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<ref bean="CustomUserDetailsService"/>
</property>
<property name="passwordEncoder">
<ref bean="passwordEncoder"/>
</property>
<property name="hideUserNotFoundExceptions">
<value>false</value>
</property>
</bean>

Disable CSRF in Java WebSecurityConfigurerAdapter. SPRING REST

Im doing a new webapp with Spring and Tiles.
So far I have the login, create/edit users working.
Now I have to start with Rest Controller for third apps with json.
And I need to disable the csrf only on the rest URL's.
I tried using the XML from spring <csrf disabled="true"/> and it works but for the enthire app is there a way to do this config by path or the only way is to write it down by Java?:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
This is not working for me, every simple example I find is the same and seems to work to everyone except me, what am i doing wrong?
spring-security config:
<?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.xsd">
<http auto-config="true">
<!-- <csrf disabled="true"/> -->
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/welcome" access="hasRole('ROLE_ADMIN')"/>
<!-- <intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" access="ROLE_USER,ROLE_ADMIN"/> -->
<intercept-url pattern="/administration/**" access="hasRole('ROLE_ADMIN')"/>
<form-login login-page="/login" default-target-url="/" authentication-failure-url="/login?error" username-parameter="username" password-parameter="password" />
<logout logout-success-url="/login?logout" />
</http>
<authentication-manager>
<authentication-provider ref="CustomAuthenticationProvider" />
</authentication-manager>
<beans:bean id="CustomAuthenticationProvider"
class="net.eqtconsulting.webapp.service.CustomAuthenticationProvider">
</beans:bean>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11" />
</beans:bean>
</beans:beans>
Also my spring-mvc
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
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">
<mvc:annotation-driven/>
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/"/>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
</mvc:interceptors>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName" value="locale" />
</bean>
<!-- Application Message Bundle -->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="WEB-INF/i18n/messages" />
<property name="fallbackToSystemLocale" value="false" />
<property name="useCodeAsDefaultMessage" value="true" />
<property name="cacheSeconds" value="3000" />
</bean>
<mvc:annotation-driven >
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.PageableHandlerMethodArgumentResolver">
<property name="maxPageSize" value="10"></property>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
<context:component-scan base-package="com.javatpoint.controller" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.tiles3.TilesViewResolver" />
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</property>
</bean>
</beans>
Im also thinking about doing it by using 2 xml servlets, but how would the spring-security work, can I make it use an other one aswell?
Added this to my xml configuration and works just fine:
<http pattern="/rest/**" security="none" />
and then the other <http... </http> config for the standard app

Unable to change Spring security access denied standard response

I have a Spring Boot application in which I used OAuth with Spring Security. When I requests an authorization token to Spring Security it returns the following response:
{"error":"invalid_grant","error_description":"Bad credentials"}
I need to change that response to a custom json, but none of the approaches I have tried works.
I have tried to use a custom AccessDeniedHandler like the following:
public class CustomOAuth2AccessDeniedHandler implements AccessDeniedHandler{
public CustomOAuth2AccessDeniedHandler() {
}
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException authException)
throws IOException, ServletException {
response.setContentType(MediaType.TEXT_PLAIN_VALUE);
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
response.getOutputStream().println("Exception with message : " + authException.getMessage());
//doHandle(request, response, authException);
}
but it does not get called. Using web.xml to redirect response is not an option for me since I am using Spring Boot, and I do not want to change the format of the response globally.
My spring-security.xml is configured like this:
<!-- Definition of the Authentication Service -->
<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 entry-point-ref="clientAuthenticationEntryPoint"/>
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="dstest"/>
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="dstest/client"/>
<property name="typeName" value="Basic"/>
</bean>
<!-- <bean id="oauthAccessDeniedHandler" -->
<!-- class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/> -->
<bean id="oauthAccessDeniedHandler"
class="in.robotrack.brad.config.CustomOAuth2AccessDeniedHandler"/>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager"/>
</bean>
<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>
<!-- Authentication in config file -->
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService"/>
</authentication-manager>
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="customUserDetailsService">
</authentication-provider>
</authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails"/>
</bean>
<!-- Token Store -->
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore"/>
<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"/>
<!-- VIV -->
<property name="accessTokenValiditySeconds" value="10"/>
</bean>
<bean id="userApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
<property name="tokenServices" ref="tokenServices"/>
</bean>
try bellow:
HttpSecurity http = ...
http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);
if you don't want to define your custom AccessdeniedHandler then even you can try this
HttpSecurity http = ...
http.exceptionHandling().accessDeniedPage("/403");
And then in your controller just define one method that can handle /403 request mapping and there you just return your 403 error page, as you do in normal methods inside any controller class.

Spring 0-Legged authentication throws error "invalid_grant" with description "Bad credentials" when obtaining OAuth token with curl

I am trying to get 0-Legged authentication to work with Spring Security and OAuth 2.0 for my RESTfull API. I am new to Spring Security curl and OAuth too, so I maybe doing something wrong, but I am trying to solve it for some time now.
When I execute curl command:
curl -X POST "http://localhost:8080/rest/oauth/token" -d \ "username=admin&password=admin&client_id=test&client_secret=test&grant_type=password"
I get following exception:
{"error":"invalid_grant","error_description":"Bad credentials"}
After some debugging I found out that error is thrown in Spring Security class AbstractUserDetailsAuthenticationProvider in method authenticate(Authentication authentication) for user credentials authentication (curl: admin, admin), client credentials (curl: test, test) are proccessed without exception.
Exception is thrown on this line:
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
due to username string being set to NONE_PROVIDED on this line:
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
After some more debuging I found out that in class Spring Security OAuth2 class ResourceOwnerPasswordTokenGranter in this method:
#Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
String username = parameters.get("username");
String password = parameters.get("password");
// Protect from downstream leaks of password
parameters.remove("password");
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
userAuth = authenticationManager.authenticate(userAuth);
}
catch (AccountStatusException ase) {
//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
throw new InvalidGrantException(ase.getMessage());
}
catch (BadCredentialsException e) {
// If the username/password are wrong the spec says we should send 400/invlid grant
throw new InvalidGrantException(e.getMessage());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
username is set to null on next line due to key in parameters beeing " username" (note white space in front) instead of "username":
String username = parameters.get("username");
So I tried to switch username and password parameters, and yes, which ever parameter is set first gets that white space in front for key value. I can work around it by adding another username parameter (or whichever is first), but that is not a solution.
Here is Spring OAuth configuration:
<?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:sec="http://www.springframework.org/schema/security"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2.xsd">
<sec:http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="authenticationManager">
<sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/>
<sec:anonymous enabled="false"/>
<sec:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
<sec:access-denied-handler ref="oauthAccessDeniedHandler"/>
</sec:http>
<sec:http pattern="/logout" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint">
<sec:anonymous enabled="false"/>
<sec:intercept-url pattern="/logout" access="ROLE_USER" method="GET"/>
<sec:logout invalidate-session="true" logout-url="/logout" success-handler-ref="logoutSuccessHandler"/>
<sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<sec:access-denied-handler ref="oauthAccessDeniedHandler"/>
</sec:http>
<sec:http pattern="/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint">
<sec:anonymous enabled="false"/>
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<sec:access-denied-handler ref="oauthAccessDeniedHandler"/>
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService"/>
</sec:authentication-manager>
<sec:authentication-manager alias="userAuthenticationManager">
<sec:authentication-provider>
<sec:password-encoder hash="bcrypt" />
<sec:jdbc-user-service data-source-ref="jdbcDataSource"
users-by-username-query="SELECT username, passhash, enabled FROM appuser WHERE USERNAME=?;"
authorities-by-username-query="SELECT u.username, r.name FROM appuser u, roletype r, appuser_roletype a WHERE u.id = a.appuser_id AND r.id = a.roletype_id AND username =?;"/>
</sec:authentication-provider>
</sec:authentication-manager>
<sec:global-method-security
secured-annotations="enabled" />
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials/>
<oauth:password authentication-manager-ref="userAuthenticationManager"/>
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" resource-id="springsec" token-services-ref="tokenServices"/>
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="accessTokenValiditySeconds" value="86400" />
<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.store.InMemoryTokenStore">
</bean>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" />
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails"/>
</bean>
<bean id="clientDetails" class="com.mytest.oauth.ClientDetailsServiceImplementation"/>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client"/>
<property name="typeName" value="Basic"/>
</bean>
<bean id="logoutSuccessHandler" class="com.mytest.oauth.LogoutImplementation">
<property name="tokenstore" ref="tokenStore"/>
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
</beans>
How can I avoid that behaviour?

Spring security custom LogoutHandler not called

I've implemented my own LogoutHandler and I'm trying to configure it in the spring security xml, but for some reason it's not being called on logout (the logout is successful, but my code isn't executed).
This is my security.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: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/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:http use-expressions="true">
<security:intercept-url pattern="/logoutSuccess"
access="permitAll" />
<security:logout logout-url="/logout"
logout-success-url="/logoutSuccess" />
</security:http>
<bean id="logoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg index="0" value="/logoutSuccess" />
<constructor-arg index="1">
<list>
<bean id="securityContextLogoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
<bean id="myLogoutHandler" class="my.package.MyLogoutHandler" />
</list>
</constructor-arg>
<property name="filterProcessesUrl" value="/logout" />
</bean>
MyLogoutHandler - this is what I want to execute on logout, but it's not being called:
import org.springframework.security.web.authentication.logout.LogoutHandler;
public class MyLogoutHandler implements LogoutHandler {
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
System.out.println("logout!");
}
}
Does anyone have any idea why it's not working? Thanks!
As you want to use your custom filter instead of spring security default log out filter, add this line to logout filter bean
<security:custom-filter position="LOGOUT_FILTER"/>
or add this line in your spring security config
<security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER"/>
Editted
<security:http use-expressions="true">
<security:intercept-url pattern="/logoutSuccess"
access="permitAll" />
<security:logout logout-url="/logout"
logout-success-url="/logoutSuccess" success-handler-ref="myLogoutHandler" />
</security:http>
<bean id="myLogoutHandler" class="my.package.MyLogoutHandler" />
Also you can implement LogoutSuccessHandler interface instead of LogoutHandler
Edit2
ok, so if you dont want to call your handler after logout is complete, remove logout tag and set everything in logout filter bean
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg index="0" value="/logoutSuccess" />
<constructor-arg index="1">
<list>
<bean id="securityContextLogoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
<bean id="myLogoutHandler" class="my.package.MyLogoutHandler" />
</list>
</constructor-arg>
<property name="filterProcessesUrl" value="/logout" />
</bean>
And add <security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER"/>

Categories

Resources