I have multiple authentication managers in the application. I distinguish them by bean name. Part of my xml configuration related to oauth authorization server looks like and it works fine:
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler" >
<oauth:authorization-code disabled="true" />
<oauth:implicit disabled="false" />
<oauth:refresh-token disabled="false" />
<oauth:client-credentials disabled="false" />
<oauth:password authentication-manager-ref="authenticationManager" />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" resource-id="resource-id" token-services-ref="tokenServices" />
<sec:authentication-manager id="clientAuthenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService" />
</sec:authentication-manager>
<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="oauthClientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
I'm trying to move it to Java based configuration (in some SecurityConfig class), without lack so far. I've tried something like:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2AuthConfig extends AuthorizationServerConfigurerAdapter {
#Resource(name = "authenticationManager")
private AuthenticationManager authenticationManager;
#Resource
private OAuth2AuthenticationEntryPoint authenticationEntryPoint;
#Resource(name = "clientDetails")
private ClientDetailsService clientDetailsService;
#Resource
private TokenStore tokenStore;
#Resource
private TokenStoreUserApprovalHandler userApprovalHandler;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.authenticationEntryPoint(authenticationEntryPoint);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userApprovalHandler(userApprovalHandler)
.tokenStore(tokenStore);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
}
#Configuration
#EnableResourceServer
protected static class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
#Resource
private DefaultTokenServices tokenServices;
#Resource(name = "authenticationManager")
private AuthenticationManager authenticationManager;
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).tokenServices(tokenServices).authenticationManager(authenticationManager);
}
}
however it still complains about multiple authentication managers, although I explicitly set endpoints.authenticationManager(authenticationManager).
With some debugging I can see it tries to configure it within class WebSecurityConfigurerAdapter and it meets multiple authentication manager within authenticationManager(). Am I able to override it or what am I missing?
AuthorizationServer - here there is a way to prevent Spring to fail on
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#authenticationManager by simply overriding method
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder) - explanation
ResourceServer - unfortunately there is no way for similar handling corresponding problem. Best what you can do is decreasing number of instances of global authentication managers to exactly one.
Related
I'm writing spring application to serve mobile as well as web portal requests.
I have added Controller to handle web portal requests and RestController to handle mobile requests. This all stuff I have done in single project.
I've configured auth.xml for authetication and all.
<security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="false" create-session="stateless" >
<security:intercept-url pattern="/api/auth" access="permitAll" />
<security:intercept-url pattern="/api/token" access="permitAll" />
<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/api/**" access="isAuthenticated()" />
<security:logout />
</security:http>
<bean class="com.auth.TokenAuthenticationFilter"
id="authenticationTokenProcessingFilter">
<constructor-arg type="java.lang.String"><value>/api/**</value></constructor-arg>
</bean>
<!-- Code for REST API Authentication -->
<!-- create-session="stateless" -->
<security:http auto-config="false" use-expressions="true" entry-point-ref="ajaxAwareAuthenticationEntryPoint" disable-url-rewriting="true">
<security:intercept-url pattern="/login" access="permitAll()" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:custom-filter position="FORM_LOGIN_FILTER" ref="authenticationFilter" />
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<security:logout logout-url="/logout" logout-success-url="/login.do" invalidate-session="true" />
<security:remember-me services-ref="rememberMeService" />
<security:session-management session-authentication-strategy-ref="sas" />
<security:csrf disabled="true"/>
</security:http>
But I want to integrate Spring OAuth 2.0 in it.
Can anyone has idea about the same ?
You can configure 2 different security filters for 2 different paths. That way, you can have differents paths of you application secured differently. Typically, you would want to have "/public/" accessible to anyone while "/api/" being secured by authentication.
I would strongly recommend to configure Spring Security in Java by extending WebSecurityConfigurerAdapter.
Here is an example Java configuration which protects only some endpoints while leaving others accessible publicly.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/**", OPTIONS.toString()),
new AntPathRequestMatcher("/public/**"),
new AntPathRequestMatcher("/health"),
// Spring Social
new AntPathRequestMatcher("/signin/**"),
new AntPathRequestMatcher("/auth/**"),
// Swagger Documentation
new AntPathRequestMatcher("/swagger-ui.html"),
new AntPathRequestMatcher("/v2/api-docs"),
new AntPathRequestMatcher("/swagger-resources/**"),
new AntPathRequestMatcher("/webjars/**")
);
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
#Autowired
private RESTAuthenticationProvider authenticationProvider;
#Autowired
private TokenService credentials;
#Autowired
private UserSecurityService users;
#Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring().requestMatchers(PUBLIC_URLS);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.exceptionHandling()
// this entry point handles when you request a protected page and you are not yet
// authenticated
.defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
.and()
.authenticationProvider(authenticationProvider)
.addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable()
.sessionManagement().disable();
}
#Bean
RESTAuthenticationFilter restAuthenticationFilter() throws Exception {
final RESTAuthenticationFilter filter =
new RESTAuthenticationFilter(PROTECTED_URLS, credentials);
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(getSuccessHandler());
return filter;
}
// Upon successful authentication, Spring will attempt to try and move you to another URL
// We have to prevent this because the request for the resource and the authentication all get done in the same request!
#Bean
SimpleUrlAuthenticationSuccessHandler getSuccessHandler() {
final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
successHandler.setRedirectStrategy(new NoRedirectStrategy());
return successHandler;
}
#Bean
AuthenticationEntryPoint forbiddenEntryPoint() {
return new Http401AuthenticationEntryPoint("Bearer");
}
}
Try out spring security. It has built in functionalities also you can always override existing behavior for your purposes.
After a research, I still dont found a solution for this problem.
My aim is to validate user in Custom Authentication Provider using database but the #Autowiring always throw Null Pointer Exception.
Here my code:
Custom Authentication Provider:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private Validator iValidate;
#Override
public Authentication authenticate(Authentication auth) throws AuthenticationException{
if (iValidate.userNameCheck(auth.getName()) != "00") {
auth=null;
}
return auth:
}
#Override
public boolean supports(Class<?> auth) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(auth));
}
}
Validator.java:
#Component
public class Validator implements IsamSvc {
private StopWatch sw = new StopWatch();
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public String userNameCheck(String mercid, String caller) {
/////validating code/////
}
}
Spring Security XML:
<global-method-security pre-post-annotations="enabled" />
<http pattern="/resources/**" security="none" />
<http auto-config="false" use-expressions="true" disable-url-rewriting="true" >
<session-management session-fixation-protection="newSession" session-authentication-error-url="/login.do?sessionalreadyexist">
<concurrency-control max-sessions="1" expired-url="/login.do?expired" error-if-maximum-exceeded="true" />
</session-management>
<intercept-url pattern="/clearcache" access="permitAll()" />
<intercept-url pattern="/login.do" access="permitAll()" />
<intercept-url pattern="/**" access="isFullyAuthenticated()" />
<logout logout-success-url="/login.do?logout" delete-cookies="JSESSIONID" invalidate-session="true" />
<access-denied-handler error-page="/403" />
<form-login login-page='/login.do' login-processing-url="/login" default-target-url="/main" always-use-default-target="true" authentication-failure-url="/login.do?error" username-parameter="username" password-parameter="password" authentication-details-source-ref="CustomAuthInfoSource" /> -->
</http>
<authentication-manager>
<authentication-provider ref="CustomAuthenticationProvider" />
</authentication-manager>
<beans:bean id="CustomAuthenticationProvider" class="xx.xxx.xxxx.xxxxx.CustomAuthenticationProvider" />
beans.xml:
<beans:bean id="iValidate" class="xx.xxx.xxxx.xxxxx.Validator" scope="prototype" />
/////// Other beans ////////
when i call #Autowired private Validator iValidate; in #Controller class, it work normally,but in CustomAuthenticationProvider, it will return null ...
Any Solution?
annotate the CustomAuthenticationProvider with #Component annotation
I'm trying to use Spring OAuth2 for my rest app.
But looks like I made a mistake and I can find where I did it.
The flow should be:
1. get token from /oauth/token with username and password
2. make request to /security with provided token
MethodSecurityConfig:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Autowired
private SecurityConfiguration securityConfig;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
OAuth2ServerConfig:
#Configuration
public class OAuth2ServerConfig {
private static final String RESOURCE_ID = "nessnity";
#Configuration
#Order(10)
protected static class UiResourceConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/security")
.and()
.authorizeRequests()
.antMatchers("/security").access("hasRole('USER')");
}
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/security/")
.and()
.authorizeRequests()
.antMatchers("/security").access("#oauth2.hasScope('read')");
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-client")
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes("client_credentials")
.authorities("ROLE_CLIENT")
.scopes("read")
.secret("password")
.accessTokenValiditySeconds(60);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm("sparklr2/client");
}
}
protected static class Stuff {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private TokenStore tokenStore;
#Bean
public ApprovalStore approvalStore() throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
#Bean
#Lazy
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
public SparklrUserApprovalHandler userApprovalHandler() throws Exception {
SparklrUserApprovalHandler handler = new SparklrUserApprovalHandler();
handler.setApprovalStore(approvalStore());
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
handler.setUseApprovalStore(true);
return handler;
}
}
}
SecurityConfiguration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("root")
.password("password")
.roles("USER");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/oauth/uncache_approvals", "/oauth/cache_approvals");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().hasRole("USER");
}
}
Problem: when I tried to get token
curl --user root:password --data "grant_type=client_credentials" http://localhost:8080/oauth/token
I got message:
{"error":"invalid_client","error_description":"Bad client
credentials"}
The second question is how to pass username/password in the url params like /oauth/token?username=root&password=password ?
Thanks.
UPDATE
I decided to start from scratch and use xml configuration.
The following configuration works perfect:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="authenticationManager"
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"/>
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<authentication-manager alias="authenticationManager"
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>
<bean id="clientDetails" class="com.nessnity.api.security.OAuthClienDetailsService">
<property name="id" value="testuser"/>
<property name="secretKey" value="secret" />
</bean>
<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="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
<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 authentication-manager-ref="userAuthenticationManager"/>
</oauth:authorization-server>
<authentication-manager id="userAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider ref="customUserAuthenticationProvider">
</authentication-provider>
</authentication-manager>
<bean id="customUserAuthenticationProvider"
class="com.nessnity.api.security.OAuthUserAuthenticationProvider">
</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="900000000"/>
<property name="clientDetailsService" ref="clientDetails"/>
</bean>
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore"/>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
</bean>
<http pattern="/resources/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false"/>
<intercept-url pattern="/resources/**" method="GET"/>
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>
<oauth:resource-server id="resourceServerFilter"
resource-id="springsec" token-services-ref="tokenServices"/>
</beans>
I have faced similar for me it worked after doing the following change
In your AuthorizationServerConfiguration class replace
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm("sparklr2/client");
}
with the below code
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//oauthServer.realm("sparklr2/client");
oauthServer.allowFormAuthenticationForClients();
}
and request should like
/oauth/token?grant_type=password&scope=read+write&client_id=yourclientId&client_secret=secret&username=userName&password=pwd
In your access token request you are using client credentials grant type. OAuth spec says that in case of client_credentials grant type you need to provide base64 encoded client_id:client_secret as Basic Authorization header.
For example if your client_id is google and client_secret is x23r-ss56-rfg8-6yt6, then you need to add these string as google:x23r-ss56-rfg8-6yt6, encode it using Base64 encoder and make request as
curl --header "Authorization: Basic <base64 encoded_string>" --data "grant_type=client_credentials" http://localhost:8080/oauth/token
I'm using Spring Security 3.2 and Spring 4.0.1
I'm working on converting an xml config into a Java config. When I annotate AuthenticationManager with #Autowired in my Filter, I'm getting an exception
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
I've tried injecting AuthenticationManagerFactoryBean but that also fails with a similar exception.
Here is the XML configuration I'm working from
<?xml version="1.0" encoding="UTF-8"?> <beans ...>
<security:authentication-manager id="authenticationManager">
<security:authentication-provider user-service-ref="userDao">
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<security:http
realm="Protected API"
use-expressions="true"
auto-config="false"
create-session="stateless"
entry-point-ref="unauthorizedEntryPoint"
authentication-manager-ref="authenticationManager">
<security:access-denied-handler ref="accessDeniedHandler"/>
<security:custom-filter ref="tokenAuthenticationProcessingFilter" position="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="tokenFilter" position="REMEMBER_ME_FILTER"/>
<security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')"/>
<security:intercept-url method="PUT" pattern="/rest/news/**" access="hasRole('admin')"/>
<security:intercept-url method="POST" pattern="/rest/news/**" access="hasRole('admin')"/>
<security:intercept-url method="DELETE" pattern="/rest/news/**" access="hasRole('admin')"/>
</security:http>
<bean class="com.unsubcentral.security.TokenAuthenticationProcessingFilter"
id="tokenAuthenticationProcessingFilter">
<constructor-arg value="/rest/user/authenticate"/>
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
<property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
</bean>
</beans>
Here is the Java Config I'm attempting
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
.and();
//TODO: Custom Filters
}
}
And this is the Custom Filter class. The line giving me trouble is the setter for AuthenticationManager
#Component
public class TokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
public TokenAuthenticationProcessingFilter(#Value("/rest/useAuthenticationManagerr/authenticate") String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
...
}
private String obtainPassword(HttpServletRequest request) {
return request.getParameter("password");
}
private String obtainUsername(HttpServletRequest request) {
return request.getParameter("username");
}
#Autowired
#Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
#Autowired
#Override
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
super.setAuthenticationSuccessHandler(successHandler);
}
#Autowired
#Override
public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
super.setAuthenticationFailureHandler(failureHandler);
}
}
Override method authenticationManagerBean in WebSecurityConfigurerAdapter to expose the AuthenticationManager built using configure(AuthenticationManagerBuilder) as a Spring bean:
For example:
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
In addition to what Angular University said above you may want to use #Import to aggregate #Configuration classes to the other class (AuthenticationController in my case) :
#Import(SecurityConfig.class)
#RestController
public class AuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
//some logic
}
Spring doc about Aggregating #Configuration classes with #Import: link
When I #Bean'ed AuthenticationManager and #Autowired it in same class then needed to activate circular references but that is rather as for CDI.
I am facing again the same problem with Spring Security: "password does not match stored value".
I import 4 accounts in my graph (I'm using SpringData/Neo4J) with my custom GraphPopulator class and try to log in with one ("fbiville"/"s3cret").
The authentication is configured as follows:
<beans:bean id="encoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder">
<beans:constructor-arg value="******" />
</beans:bean>
<beans:bean id="userService" class="com.lateralthoughts.devinlove.service.LoginService" />
<authentication-manager>
<authentication-provider user-service-ref="userService">
<password-encoder ref="encoder" />
</authentication-provider>
</authentication-manager>
And the class in charge of persisting accounts is partially based on a custom SpringData repository implementation:
class PersonRepositoryImpl implements PersonRepositoryCustom {
private final PasswordEncoder passwordEncoder;
private final Neo4jOperations template;
#Autowired
public PersonRepositoryImpl(PasswordEncoder passwordEncoder,
Neo4jOperations template) {
this.passwordEncoder = passwordEncoder;
this.template = template;
}
#Override
#Transactional
public void persist(Person person) {
person.setPassword(passwordEncoder.encode(person.getPassword()));
template.save(person);
}
}
Finally, the login process is configured as follows:
<http auto-config='true' use-expressions='true' realm="devinlove: love is just another algorithm">
<form-login login-page="/login"
default-target-url="/"
authentication-failure-url="/login" />
<intercept-url pattern="/login" access="isAnonymous()" />
<intercept-url pattern="/**" access="hasRole('USER')" />
</http>
I debugged the StandardPasswordEncoder at account creation and user login attempt, and I noticed the salts don't match, which obviously leads to an authentication error.
You can clone the repository if you wanna reproduce the problem.
Thanks in advance !
Rolf
You need to use org.springframework.security.authentication.encoding.PasswordEncoder implementation, rather then org.springframework.security.crypto.password.PasswordEncoder.
So you can simply use as the follows:
<authentication-manager>
<authentication-provider user-service-ref="userService">
<password-encoder hash="sha" base64="true">
<!--Single salt - the very same as you are using in `encoder` instance-->
<salt-source system-wide="********"/>
</password-encoder>
</authentication-provider>
</authentication-manager>
Note, that this will work properly with #Autowired properties, though there shouldn't be #Qualifier and beans have to be represented only once.
It exposes implementation of such interfaces: org.springframework.security.authentication.encoding.PasswordEncoder and org.springframework.security.authentication.dao.SaltSource.
So in your particular case it would look like as follows:
class PersonRepositoryImpl implements PersonRepositoryCustom {
private final PasswordEncoder passwordEncoder;
private final Neo4jOperations template;
#Autowired
public PersonRepositoryImpl(PasswordEncoder passwordEncoder,
SaltSource saltSource
Neo4jOperations template) {
this.passwordEncoder = passwordEncoder;
this.saltSource = saltSource;
this.template = template;
}
#Override
#Transactional
public void persist(Person person) {
person.setPassword(passwordEncoder.encode(person.getPassword(), saltSource));
template.save(person);
}
}