Override AuthorizationEndpoint with custom implementation - java

I'd like to override spring's default AuthorizationEndpoint and provide my own on /oauth/authorize. I wrote my own controller
#RestController
#RequestMapping("oauth/authorize")
public class AuthorizationController {
#RequestMapping
public void authorize(#RequestParam Map<String, String> parameters, HttpServletResponse response) throws Exception {
// todo
}
}
However it is not mapped as AuthorizationEndpoint maps to /oauth/authorize by default. How can I remove the standard implementation?
Bonus
The reason I want to provide my own implementation is because my rest api is stateless and does not provide sessions and/or web interface, standalone angular app does that for me and authorizes using passwrd grant to server. So what I want to do is redirect the user to my angular app's approoval page and implement a custom user_oauth_approval approveOrDeny endpoint which my client calls. I'm not sure if I can set that up with spring, and even if I could, custom implementation would probably be less hassle. I'd love to hear some insights

Inject your customAuthenticationManager in your new controller.
#Autowired
private AuthenticationManager authenticationManager;
#RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtAuthenticationRequest authenticationRequest)
throws AuthenticationException {
Authentication customAuthentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(), authenticationRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
return ResponseEntity
.ok(new JwtAuthenticationResponse(customAuthentication.getToken(), customAuthentication.isActive()));
}
Then overwrite default Spring AuthenticationManager + AuthenticationProvider
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider);
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
import org.springframework.security.authentication.AuthenticationProvider;
#Component("customAuthenticationProvider")
public class CustomAuthenticationProvider implements AuthenticationProvider {

Related

Prevent spring from injecting the default AuthenticationManager

I'm trying to create a spring security configuration with two different AuthenticationProviders and exposing a rest interface to verify credentials (this is just used in the dev environment and will be replaced by an oAuth service in prod.) But when I inject the AuthenticationManager into the Controller, spring creates a default AuthenticationManager and injects it into the RestController. How can I make spring inject the AuthenticationManager configured in the WebSecurityConfigurationAdapter? I'm using spring-boot-starter-security:1.5.7.RELEASE. Here is my security configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#Configuration
public class LocalWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
private final DevUserDetailsService devUserDetailService;
private final ServiceUserDetailService serviceUserDetailService;
#Autowired
public LocalWebSecurityConfigurationAdapter(DevUserDetailsService devUserDetailService, ServiceUserDetailService serviceUserDetailService) {
this.devUserDetailService = devUserDetailService;
this.serviceUserDetailService = serviceUserDetailService;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers("/api/public/**").permitAll()
.antMatchers("/api/login").permitAll()
.antMatchers("/api/**").fullyAuthenticated()
.anyRequest().permitAll()
.and().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint())
.and().httpBasic();
}
#Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(devUserDetailService);
DaoAuthenticationProvider serviceUserAuthProvider = new DaoAuthenticationProvider();
serviceUserAuthProvider.setUserDetailsService(serviceUserDetailService);
serviceUserAuthProvider.setPasswordEncoder(passwordEncoder());
auth.authenticationProvider(serviceUserAuthProvider);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
And here is my RestController:
#RestController
#RequestMapping("/api/login")
public class LoginController {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final AuthenticationManager authenticationManager;
public LoginController(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#RequestMapping(method = RequestMethod.POST)
public Map<String, String> login(#RequestBody Map<String, String> body) {
String user = body.get("user");
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, body.get("password"));
try {
authenticationManager.authenticate(token);
return Collections.singletonMap("status", "ok");
} catch (BadCredentialsException e) {
return Collections.singletonMap("status", "bad credentials");
} catch (AuthenticationException e) {
log.warn("Could not authenticate user {} because {}.", user, e.getMessage(), e);
return Collections.singletonMap("status", "general error");
}
}
}
And since you guys are probably experts in spring, is there a best practice to create different security configurations depending on the environment (using the profile) the code is running on without creating redundant code? I tried a super class, but spring didn't like that a lot.
I finally found a solution. By using configureGlobal inside my configuration class the AuthenticationManager gets shared across all spring managed Components.
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DevUserDetailsService devUserDetailService,
#Qualifier("serviceUserAuthenticationProvider") AuthenticationProvider serviceUserAuthProvider) throws Exception {
auth.userDetailsService(devUserDetailService);
auth.authenticationProvider(serviceUserAuthProvider);
}
For reusing configuration, I still didn't find a good solution. Creating an abstract “super configuration” for all the common configuration is creating troubles as soon as a method is annotated with #Bean and creating multiple WebSecurityConfigurerAdapter results in one overwriting the other, so if there is a best practice, I'm still interested in a proper solution. I've managed to do what I wanted, but it still feels like a little of a hack to me. For anyone stumbling across a similar issue I hope this helps a little.
Declare the bean in LocalWebSecurityConfigurationAdapter:
#Bean(name="appAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
and inject it other components just like any other bean:
public LoginController(#Qualifier("appAuthenticationManager") AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}

Spring Boot: Autowired CRUDRepository null, in certain classes

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");
}
}

Use #WithMockUser (with #SpringBootTest) inside an oAuth2 Resource Server Application

Environment:
I have a spring boot based microservice architecture application consisting of multiple infrastructural services and resource services (containing the business logic). Authorization and authentication is handled by an oAuth2-Service managing the user entities and creating JWT tokens for the clients.
To test a single microservice application in its entirety i tried to build tests with testNG, spring.boot.test, org.springframework.security.test ...
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, properties = {"spring.cloud.discovery.enabled=false", "spring.cloud.config.enabled=false", "spring.profiles.active=test"})
#AutoConfigureMockMvc
#Test
public class ArtistControllerTest extends AbstractTestNGSpringContextTests {
#Autowired
private MockMvc mvc;
#BeforeClass
#Transactional
public void setUp() {
// nothing to do
}
#AfterClass
#Transactional
public void tearDown() {
// nothing to do here
}
#Test
#WithMockUser(authorities = {"READ", "WRITE"})
public void getAllTest() throws Exception {
// EXPECT HTTP STATUS 200
// BUT GET 401
this.mvc.perform(get("/")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
}
}
where the security (resource server) config is the following
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
// get the configured token store
#Autowired
TokenStore tokenStore;
// get the configured token converter
#Autowired
JwtAccessTokenConverter tokenConverter;
/**
* !!! configuration of springs http security !!!
*/
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/**").authenticated();
}
/**
* configuration of springs resource server security
*/
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// set the configured tokenStore to this resourceServer
resources.resourceId("artist").tokenStore(tokenStore);
}
}
and the following method based security check annotated inside the controller class
#PreAuthorize("hasAuthority('READ')")
#RequestMapping(value = "/", method = RequestMethod.GET)
public List<Foo> getAll(Principal user) {
List<Foo> foos = fooRepository.findAll();
return foos;
}
I thought that would work but when running the test i only get an assertion error
java.lang.AssertionError: Status
Expected :200
Actual :401
Question:
Is there something totally obvious that i am doing wrong? Or is #WithMockUser not going to work with #SpringBootTest and #AutoConfigureMockMvc in an oAuth2 environment? If this is the case... what would be the best approach for testing route and method based security configurations as part of such an (integration) test like this one?
Appendix:
I also tried different approaches like something like the following... but it led to the same result :(
this.mvc.perform(get("/")
.with(user("admin").roles("READ","WRITE").authorities(() -> "READ", () -> "WRITE"))
.accept(MediaType.APPLICATION_JSON))
see:
spring security testing
spring boot 1.4 testing
#WithMockUser creates the authentication in SecurityContext.
Same applies for with(user("username")).
By default the OAuth2AuthenticationProcessingFilter does not use the SecurityContext, but always build the authentication from the token ("stateless").
You can easily change this behavior be setting the stateless flag in the resource server security configuration to false:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration implements ResourceServerConfigurer {
#Override
public void configure(ResourceServerSecurityConfigurer security) throws Exception {
security.stateless(false);
}
#Override
public void configure(HttpSecurity http) {}
}
Another option is to extend ResourceServerConfigurerAdapter, but the problem with that is that it comes with configuration that forces all requests to be authenticated. Implementing the interface leaves your main security config unchanged apart from the statelessness.
Of course, set the flag to to false in your test contexts, only.
I had de same issue, and the only way I found was creating a token and using it in the mockMvc perform
mockMvc.perform(get("/resource")
.with(oAuthHelper.bearerToken("test"))
And the OAuthHelper:
#Component
#EnableAuthorizationServer
public class OAuthHelper extends AuthorizationServerConfigurerAdapter {
#Autowired
AuthorizationServerTokenServices tokenservice;
#Autowired
ClientDetailsService clientDetailsService;
public RequestPostProcessor bearerToken(final String clientid) {
return mockRequest -> {
OAuth2AccessToken token = createAccessToken(clientid);
mockRequest.addHeader("Authorization", "Bearer " + token.getValue());
return mockRequest;
};
}
OAuth2AccessToken createAccessToken(final String clientId) {
ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
Collection<GrantedAuthority> authorities = client.getAuthorities();
Set<String> resourceIds = client.getResourceIds();
Set<String> scopes = client.getScope();
Map<String, String> requestParameters = Collections.emptyMap();
boolean approved = true;
String redirectUrl = null;
Set<String> responseTypes = Collections.emptySet();
Map<String, Serializable> extensionProperties = Collections.emptyMap();
OAuth2Request oAuth2Request = new OAuth2Request(requestParameters, clientId, authorities,
approved, scopes, resourceIds, redirectUrl, responseTypes, extensionProperties);
User userPrincipal = new User("user", "", true, true, true, true, authorities);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities);
OAuth2Authentication auth = new OAuth2Authentication(oAuth2Request, authenticationToken);
return tokenservice.createAccessToken(auth);
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test")
.authorities("READ");
}
}
As I was specifically trying to write tests against our ResourceServerConfiguration, I worked around the issue by creating a test wrapper for it which set security.stateless to false:
#Configuration
#EnableResourceServer
public class ResourceServerTestConfiguration extends ResourceServerConfigurerAdapter {
private ResourceServerConfiguration configuration;
public ResourceServerTestConfiguration(ResourceServerConfiguration configuration) {
this.configuration = configuration;
}
#Override
public void configure(ResourceServerSecurityConfigurer security) throws Exception {
configuration.configure(security);
security.stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
configuration.configure(http);
}
}

Using #PreAuthorize or #Secured with Jersey when using Configuration Class

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

Spring Security, Boot: replace default DaoAuthenticationProvider

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)

Categories