Right after registration (sign up) I'm logging in my user programmatically via Spring Security:
public register(HttpServletRequest request, String user, String password) {
...
request.login(user, password);
}
This works fine, but it doesn't create the remember-me cookie (although with interactive login the cookie is created fine).
Now I've read in this and this answer, that you have to wire in the implementation of RememberMeServices (I use PersistentTokenBasedRememberMeServices) and then call onLoginSuccess. I haven't been successful to autowire PersistentTokenBasedRememberMeServices.
How to make this work? Is this the right way? Why Spring Security doesn't offer a more convenient way?
P.S.: This is an excerpt from my configuration:
#Configuration
#EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.tokenRepository(new MyPersistentTokenRepository())
.rememberMeCookieName("rememberme")
.tokenValiditySeconds(60 * 60 * 24)
.alwaysRemember(true)
.useSecureCookie(true)
.and()
....
...
}
}
You didn't mention the Spring version. Below configuration will work with Spring 4 but you can modify it for other version. In your WebSecConf class autowire PersistentTokenRepository and UserDetailsService interfaces. Add Bean to get PersistentTokenBasedRememberMeServices instance.
#Configuration
#EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {
#Autowired
PersistentTokenRepository persistenceTokenRepository;
#Autowired
UserDetailsService userDetailsService;
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.tokenRepository(persistenceTokenRepository)
.rememberMeCookieName("rememberme")
.tokenValiditySeconds(60 * 60 * 24)
.alwaysRemember(true)
.useSecureCookie(true)
.and()
....
...
}
#Bean
public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices persistenceTokenBasedservice = new PersistentTokenBasedRememberMeServices("rememberme", userDetailsService, persistenceTokenRepository);
persistenceTokenBasedservice.setAlwaysRemember(true);
return persistenceTokenBasedservice;
}
}
Now in your Controller or class where you are doing programmatic login, autowire PersistentTokenBasedRememberMeServices and add below code inside the method to invoke loginSuccess method.
#Autowired
PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices;
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
persistentTokenBasedRememberMeServices.loginSuccess(request, response, auth);
}
I've stumbled on this issue and struggled a bit to get everything working correctly, for future reference this is how to set things up.
Define a RememberMeService bean configured to your needs.
Use TokenBasedRememberMeServices if you want a simple hash based token system or PersistentTokenBasedRememberMeServices if you'd rather persist the tokens to database. Both solutions are described in further details here : https://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/remember-me.html
Please note that the constructor first argument is not the cookie name but the key used to validate remember-me tokens.
#Configuration
public class SecurityBeans {
#Autowire
PersistentTokenRepository persistenceTokenRepository;
#Autowired
UserDetailsService userDetailsService;
#Bean
public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices persistenceTokenBasedservice = new TokenBasedRememberMeServices("remember-me-key", userDetailsService, persistenceTokenRepository);
persistenceTokenBasedservice.setCookieName("rememberme");
persistenceTokenBasedservice.setTokenValiditySeconds(60 * 60 * 24);
persistenceTokenBasedservice.setAlwaysRemember(true);
persistenceTokenBasedservice.setUseSecureCookie(true);
return persistenceTokenBasedservice;
}
}
You should inject the RememberMeService directly when configuring HttpSecurity. You also have to configure the exact same key as defined in your RememberMeService because the configurer also sets up the RememberMeAuthenticationProvider which checks that the remember-me token key generated by RememberMeService is correct.
#Configuration
#EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {
#Autowired
RememberMeServices rememberMeServices;
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.rememberMeServices(rememberMeServices)
.key("remember-me-key")
.and()
....
...
}
}
And finally you should invoke RememberMeService's loginSuccess in your method doing the programmatic login as described in abaghel's answer.
Related
I want to make a rest API where some endpoints are protected by an authentication base on users roles. I also would like to use JWT token and to check roles of user. Furthermore, I have seen countless confusing things in internet that are either depreciating or not working in my case.
My goal with this question is to get a better understanding of how spring work to generate JWT, how to generate one if user is login and how to make endpoint inaccessible if you don't have right. And most importantly, how to do that with last and best security standards and best code quality for the last version of spring (3.0.1).
I have tried to use spring security module with SecurityConfig class that has #EnableWebSecurity and I have tried to define a simple filter chain, but It seems to return me 401 error even if I'm on authorized endpoint.
Here is my conf :
#EnableWebSecurity
#Configuration
public class SecurityConfig {
#Autowired
DataSource dataSource;
#Autowired
UserDetailsService userDetailsService;
#Autowired
PasswordEncoder passwordEncoder;
#Autowired
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
return auth.build();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeHttpRequests().requestMatchers("/api/auth/login").permitAll();
http.authorizeHttpRequests().requestMatchers("/api/auth/register").permitAll();
http.authorizeHttpRequests().anyRequest().authenticated();
return http.build();
}
}
As you can see I provide a bean that provide UserDetailsService that I think can be used to check if user as a correct password. So here is my Service implementation :
#Service
public class MyUserDetailsService implements UserDetailsService {
private final UserAuthService userAuthService;
public GroMedUserDetailsService(UserAuthService userAuthService){
this.userAuthService = userAuthService;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> user = this.userAuthService.findUserByUsername(username);
if(user.isEmpty()){
throw new UsernameNotFoundException("User not found");
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(user.get().getGroMedRole().toString()));
return new org.springframework.security.core.userdetails.User(user.get().getUsername(),user.get().getPassword(),authorities);
}
}
So I know I don't have all of component needed for authentification. But I'm here to learn how to correctly create it and use it. So if you know some good tutorial or you have a complete awnser for me I will be please to read it.
Hello I'have a web application secured with Spring security, with a login page. This is my Security Configuration
#Configuration
#ComponentScan("it.besmart")
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
CustomSuccessHandler customSuccessHandler;
#Autowired
CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
#Autowired
DataSource dataSource;
#Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
#Autowired
private UsersConnectionRepository usersConnectionRepository;
#Autowired
private FacebookConnectionSignup facebookConnectionSignup;
private final static Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
#Autowired
public void configureGlobalService(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Webapp security configured");
http.
authorizeRequests()
.antMatchers("/", "/register", "/registrationConfirm", "/resendRegistrationToken", "/park/**")
.permitAll()
.antMatchers("/edit/**", "/payment/**", "/plate/**", "/book/**", "/home", "/stop/**",
"/notification/**", "/include/**")
.access("hasRole('USER') or hasRole('ADMIN') or hasRole('PARK')").antMatchers("/admin/**")
.access("hasRole('ADMIN') or hasRole('PARK')").antMatchers("/updatePassword")
.hasAuthority("CHANGE_PASSWORD_PRIVILEGE")
.and().formLogin().loginPage("/")
.successHandler(customSuccessHandler).failureHandler(customAuthenticationFailureHandler)
.usernameParameter("email").passwordParameter("password").and().rememberMe()
.rememberMeParameter("remember-me").tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(86400).and().exceptionHandling().accessDeniedPage("/Access_Denied").and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/?logout=true").permitAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(dataSource);
return db;
}
}
This works good by securing all my web application.
In the same application I have also a Resource/Authorization Server to protect some REST api.
Some resources are protected with an authorization code grant, so the untrusted Mobile App should take the access token from my application with a login form. I would like that the application use a different login page when trying to login from the Mobile App.
This is my resourceServer configuration
#EnableResourceServer
#ComponentScan("it.besmart.easyparking")
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig {
private final Logger logger = LoggerFactory.getLogger(ResourceServerConfig.class);
#Autowired
DataSource dataSource;
private static final String RESOURCE_ID = "easyparking_api";
#Configuration
// #Order(2)
public class grantCredentialsConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
logger.debug("Api security configured");
http
.requestMatchers().antMatchers("/api/oauth/**").and().authorizeRequests()
.antMatchers("/api/oauth/**").access("hasRole('USER')").and().formLogin().loginPage("/apilogin")
.permitAll();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore()).resourceId(RESOURCE_ID);
}
}
#Configuration
// #Order(4)
public class clientCredentialsConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
logger.debug("Client security configured");
http
.requestMatchers().antMatchers("/oauth2/**", "/api/registration", "/api/park/**").and()
.authorizeRequests().antMatchers("/oauth2/**", "/api/registration", "/api/park/**").authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore()).resourceId(RESOURCE_ID);
}
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
so, grantCredentialsConfiguration should redirect the requests to /apilogin form, but it does not, i am redirected to the main web app login page... How it can be accomplished?
EDIT
Looking closer into the logs, it looks like that when i try to hit /oauth/authorize/ the normal security chain takes place and i get
2017-05-25 12:23:15 DEBUG o.s.security.web.FilterChainProxy[310] - /oauth/authorize?response_type=token&client_id=test&redirect_uri=https://www.getpostman.com/oauth2/callback reached end of additional filter chain; proceeding with original chain
2017-05-25 12:23:15 DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping[310] - Looking up handler method for path /oauth/authorize
2017-05-25 12:23:15 DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping[317] - Returning handler method [public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(java.util.Map<java.lang.String, java.lang.Object>,java.util.Map<java.lang.String, java.lang.String>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)]
2017-05-25 12:23:15 DEBUG o.s.s.w.a.ExceptionTranslationFilter[163] - Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.authentication.InsufficientAuthenticationException: User must be authenticated with Spring Security before authorization can be completed.
So it looks like searching for a handler to manage the request, instead of redirecting to /api/apilogin as desired, he finds an Authentication exception and so i go to the standard login page... But why i get this exception?
Its happening because you haven't specified the order of the security configuration classes.
In Spring security resources protection should be mentioned from specific to generic.
Class SecurityConfiguration is more generic than grantCredentialsConfiguration. As both protect following resources.
SecurityConfiguration protects /** (Default URL)
grantCredentialsConfiguration /api/oauth/**
Since the order is not defined, SecurityConfiguration's generic configuration hides the specific configuration by grantCredentialsConfiguration
To get these to work as expected you'll have to define the order as below.
#Configuration
#Order(2)//Generic config should have larger value (lower priority)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
}
#Configuration
#Order(1)//Specific with lower value (higher priority)
public class grantCredentialsConfiguration extends ResourceServerConfigurerAdapter {
}
Note: Since these login pages are not from different applications, they share the SecurityContextHolder or the security context. So if you login from one login page and then try to go the protected resource of the other, you won't be redirected to the next login page. Instead you'll get the 403 (depending on the roles assigned by the different login pages). At a time only one login session can be maintained.
Here's a sample on Github
https://github.com/ConsciousObserver/TestMultipleLoginPages.git
Have you tried adding URL path /apilogin , to the
.antMatchers("/", "/register", "/registrationConfirm",/resendRegistrationToken", "/park/**")
.permitAll()
I am guessing the application is redirecting the /apilogin access to the common authentication login page, since it is not added to the unauthenticated access list.
I'm running a SpringBoot application with a the root class being annotated with #SpringBootApplication, #EnableAutoConfiguration.
I've created a UserRepositoryInterface interface, which extends the CrudRepository interface with my User JPA object. There is no implementation for this interface and there was no need for one yet. Neither are there any configuration files for anything in this Application. Except for the JPA DB connection, but that works.
public interface UsersRepositoryInterface extends CrudRepository<User, Long> {
// Query to search for users via email
List<User> findByEmail(#Param("email") String email);
}
And I've succesfully Autowired it into some REST endpoints. The problem appears, when I try to Autowire it into my security classes. I'm trying for authentication with JWT and it works. Now I want to call the database during the log-in process and I'm having an issue. Here are the classes:
First the WebSecurityConfiguererAdapter class, where I add the paths to the filter. Note the line with "new JWTLoginFilter", which is the class where I try to Autowire:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsServ;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
//Allow options pre-flight request
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// Allow POST request to /login
.antMatchers(HttpMethod.POST, "/login").permitAll()
// Others must be authenticated
.anyRequest().authenticated()
.and()
// We filter the api/login requests
.addFilterBefore(new JWTLoginFilter("/login", authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
// And filter other requests to check the presence of JWT in header
.addFilterBefore(new JWTAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Change logging in from username+password to email+password
auth.userDetailsService(userDetailsServ);
}
}
And the JWTLoginFilter class. I ommited some irrelevant code:
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
private UsersRepositoryInterface userRepo;
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(
HttpServletRequest req, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
//Check if userRepo is injected
if(userRepo == null) {
System.out.println("Null");
}
AccountCredentials creds = new ObjectMapper()
.readValue(req.getInputStream(), AccountCredentials.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
creds.getEmail(),
creds.getPassword(),
Collections.emptyList()
)
);
}
}
The println in JWTLoginFilter will always return Null, when called upon.
Am I missing something?
SOLVED IT:
Works now.
Annotated the JWTLoginFilter with
#Component("someName")
And injected it in the WebSecurityConfig with
#Resource(name="someName")
private JWTLoginFilter myFilter;
Hardcoded the URL in the JWTLoginFilter constructor, but I still had to Autowire the AuthenticationManager from the WebSecurityConfig into the JWTLoginFilter.
First had to make the AuthenticationManager a Bean. Used the answer here: How To Inject AuthenticationManager using Java Configuration in a Custom Filter
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Then injected it with answer here: Spring Security authenticationmanager must be specified - for custom filter
#Override
#Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
While removing the
setAuthenticationManager(authManager);
in the constructor in JWTLoginFilter
Well, what do you expect? You are creating the JWTLoginFilter via the new keyword. Spring does not do any wiring here at all. You should make this filter a #Bean or #Component or whatever else to make it a spring bean and inject it into WebSecurityConfig somehow.
I Hope your problem has been resolved already. But I am adding a sample code snippet for reference for those who are facing the same issue.
When our filter contains Autowired dependencies, and instantiating our filter in spring configuration using new() would not autowire its dependencies as this won't be a string managed bean. Hence we need to autowire our filter in spring application configuration class , which in turn autowire its internal dependencies.
PFB the sample code for reference.
#SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
#Autowired
private MyFilter myFilter;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(MyApplication.class);
}
#Bean
public FilterRegistrationBean myFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(myFilter);
registration.addUrlPatterns("*");
return registration;
}
}
Filter:
#Component
public class MyFilter extends BaseFilter {
#Autowired
private EmployeeRepository employeeRepository;
//your code goes here
if(employeeRepository != null) {
System.out.println("employeeRepository is not null");
}
}
I am having a problem similar to PreAuthorize annotation doesn't work with jersey. I created a configuration class for Spring Security and the authentication works but the authorization does not.
Here is my code
SpringSecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(1)
#ComponentScan({"com.foo.rest.resources.Template"})
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userService;
private final TokenAuthenticationService tokenAuthenticationService;
public SpringSecurityConfig() {
super(true);
this.userService = new UserService();
tokenAuthenticationService = new TokenAuthenticationService("tooManySecrets", userService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().and()
.anonymous().and()
.servletApi().and()
.headers().cacheControl().and()
.authorizeRequests()
// Allow anonymous logins
.antMatchers("/auth/**").permitAll()
// All other request need to be authenticated
.anyRequest().authenticated().and()
// Custom Token based authentication based on the header previously given to the client
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
#Override
public UserService userDetailsService() {
return userService;
}
#Bean
public TokenAuthenticationService tokenAuthenticationService() {
return tokenAuthenticationService;
}
}
and Template.java
#Component
#Path("/template")
#Produces(MediaType.APPLICATION_JSON)
public class Template {
#GET
#Secured("ROLE_EDITOR")
public User getTemplate() {
return new Template();
}
}
My guess is that the authentication is handled in the filter chain but it never comes back around after the authorization tag is reached. Any idea how to make this work?
I think your #ComponentScan is configured wrongly and doesn't pick the Template resource correctly.
According to #ComponentScan documentation the value is an alias for basePackages but you have given a Class instead of Package. Try and change it to look like following and see.
#ComponentScan({"com.foo.rest.resources.*"})
And make sure you haven't missed any steps in Jersey Spring Integration as per the documentation
I am trying to add user ip verification during login process. If ip address of the user is not in the database the application should reject the authentication.
The problem: Given the setup below it turns out that auth.authenticationProvider() is not replacing the default DaoAuthenticationProvider, but adds UserIpAuthenticationProvider as a first AuthenticationProvider in the list.
In the case when username/password combination is incorrect the framework ends up calling UserDetailsService.loadUserByUsername() twice, once from UserIpAuthenticationProvider, another time from internal DaoAuthenticationProvider which throws the final BadCredentialsException().
The question: is there any setting that can be set in Spring Boot so that Spring Security does not add it's own internal instance DaoAuthenticationProvider, but only use my UserIpAuthenticationProvider, which already has all the necessary functionality (perhaps by somehow replacing AuthenticationManagerBuilder to be able to override userDetailsService() method?).
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T> userDetailsService(
T userDetailsService) throws Exception {
this.defaultUserDetailsService = userDetailsService;
return apply(new DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>(userDetailsService));
}
Configuration: In my understanding, UserDetailsService is supposed to provide all the necessary details about the user so that AuthenticationProvider can make a decision whether the authentication was successful or not.
Since all the necessary information is loaded from the database, it seems natural to extend DaoAuthenticationProvider and add an additional verification in overriden additionalAuthenticationChecks() method (white-listed IP list is in the database, so they are loaded as part of the user object in IpAwareUser).
#Named
#Component
class UserIpAuthenticationProvider extends DaoAuthenticationProvider {
#Inject
public UserIpAuthenticationProvider(UserDetailsService userDetailsService)
{
...
}
#SuppressWarnings("deprecation")
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
super.additionalAuthenticationChecks(userDetails, authentication);
WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();
IpAwareUser ipAwareUser = (IpAwareUser) userDetails;
if (!ipAwareUser.isAllowedIp(details.getRemoteAddress()))
{
throw new DisabledException("Login restricted from ip: " + details.getRemoteAddress());
}
}
}
This is injected into SecurityConfiguration:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilter(authenticationFilter);
http.authorizeRequests()
.antMatchers("/", "/javascript/**", "/css/**").permitAll()
.antMatchers("...").access("...")
.anyRequest().authenticated()
.and().formLogin().loginPage("/").permitAll()
.and().logout().invalidateHttpSession(true).deleteCookies("JSESSIONID").permitAll()
.and().csrf().disable()
;
}
#Inject
private UserDetailsService userDetailsService;
#Inject
private UserIpAuthenticationProvider userIpAuthenticationProvider;
#Inject
private JsonUsernamePasswordAuthenticationFilter authenticationFilter;
#Bean
public JsonUsernamePasswordAuthenticationFilter authenticationFilter() {
return new JsonUsernamePasswordAuthenticationFilter();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(userIpAuthenticationProvider);
auth.userDetailsService(userDetailsService);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() throws Exception {
return new JsonAuthenticationSuccessHandler();
}
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() throws Exception {
return new JsonAuthenticationFailureHandler();
}
}
and application configuration:
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackageClasses = {SecurityConfiguration.class, DataController.class, DaoService.class})
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application;
}
}
Any guidance on this will be much appreciated.
The comments on the question contained the answer:
#ArunM: your project gave me an idea: I do not need to call auth.userDetailsService(userDetailsService); in SecurityConfiguration.configure(), which will prevent creation of internal DaoAuthenticationProvider! My UserIpAuthenticationProvider can get instance of UserDetailsService via injection.
The AuthenticationManagerBuilder.userDetailsService method does not only set the default UserDetailsService but also applies a DaoAuthenticationConfigurer which registers the DaoAuthenticationProvider.
If you want a customized DaoAuthenticationProvider, pass the UserDetailsService to the provider in the constructor or inject it. And to prevent the default DaoAuthenticationProvider from being registered, don't call AuthenticationManagerBuilder.userDetailsService.
This is also mentioned in this Spring Security issue.
Defining your own DaoAuthenticationProvider
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
return new UserIpAuthenticationProvider();
}
should replace the Spring Boot default instance (not that the bean type is DaoAuthenticationProvider and not UserIpAuthenticationProvider)