I'm fairly new at the whole Spring Security thing, and I'm trying to set up a website that uses pre-authentication. I have followed some examples and tutorials and I think it's going well. But i have stucked on one thing.
When i navigate through the .jsp pages everything works fine. I can access the "public" pages, and i get "Access Denied" when i try to access the "private" pages (I haven't finished the filter for private pages).
But if I check the logs when navigating I get the same error everytime accessing a page:
org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
The thing is, I have a Provider for this, or at least I think so.
SecurityConfig.java:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean(name = "myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
#Bean
public AccessDecisionManager accessDecisionManager()
{
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(new RoleVoter());
return new AffirmativeBased(decisionVoters);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
DlaUserDetailsService userDetailsService = new DlaUserDetailsService();
provider.setPreAuthenticatedUserDetailsService(userDetailsService);
auth.authenticationProvider(provider);
}
#Override
public void configure(HttpSecurity http) throws Exception
{
DlaSpringMvcFilter filter = new DlaSpringMvcFilter();
AuthenticationManager authenticationManager = this.authenticationManager();
filter.setAuthenticationManager(authenticationManager);
http.addFilter(filter).authorizeRequests()
.antMatchers("/private/**").hasRole("ADMIN")
.antMatchers("/public/**").permitAll();
http.csrf().disable();
}
}
What have I done wrong here? Am I missing something?
Related
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;
}
I'm struggling with the problem from the title for few days already and I'm pretty frustrated. I have no idea what I'm doing wrong and why my implementation isn't working.
Let me show you what I've got:
Custom AuthenticationProvider:
#Component
public class AuthProvider implements AuthenticationProvider {
private Logger logger = LoggerFactory.getLogger(AuthProvider.class);
public AuthProvider() {
logger.info("Building...");
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
logger.info("Authenticate...");
return null;
}
public boolean supports(Class<?> authentication) {
logger.info("Supports...");
return true;
}
}
WebSecurity config:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthProvider authProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated();
}
}
As you can see I've added loggers into the AuthenticationProvider but not any of them is getting called.
What I've tried:
adding #Autowired to configure where the AuthenticationManagerBuilder is
adding #EnableGlobalMethodSecurity(prePostEnabled=true) to the class
adding custom AuthenticationProvider directly to HttpSecurity
How I've tested it:
debugging via IntelliJ - no results, no breakpoint is getting called.
running the app and sending a request - also no results, no logs, nothing.
Please guys help me somehow. I'm outta energy. I hate wasting so much time on things that should just work :(
Probably you missed the following method in your WebSecurityConfigurerAdapter:
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
The same happened to me.
Using the isAssignableFrom() method instead of instead of == or equals we get a true, then the flow would pass through authenticate()
override fun supports(authentication: Class<*>): Boolean {
return UsernamePasswordAuthenticationToken::class.java.isAssignableFrom(authentication)
}
GL
Source
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've been trying to get OAuth 2 working for my application but I continue running into configuration-related errors, specifically involving authentication tokens. The application is set up to act as both authorization and resource server. I've successfully configured it to issue tokens using password grant type, with an in-memory token store. However, every time I try to send requests for restricted resources, I get errors saying:
org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
So, I tried setting up a PreAuthenticationAuthenticationProvider in my configuration:
#Autowired
private UserDetailsManager userManager;
#Bean
public AuthenticationProvider preAuthenticationAuthenticationProvider() {
PreAuthenticatedAuthenticationProvider authenticationProvider =
new PreAuthenticatedAuthenticationProvider();
UserDetailsByNameServiceWrapper userDetailsWrapper = new UserDetailsByNameServiceWrapper(userManager);
authenticationProvider.setPreAuthenticatedUserDetailsService(userDetailsWrapper);
return authenticationProvider;
}
However, I'm getting NullPointerException in weird places, like:
java.lang.NullPointerException: null
at org.springframework.security.authentication.AccountStatusUserDetailsChecker.check(AccountStatusUserDetailsChecker.java:17) ~[spring-security-core-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
I'm wondering what the simplest configuration for this is, and why I need it in the first place? Is it because I have #PreAuthorize annotations?
Here's how I set up the resource server:
#Configuration
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore).authenticationManager(authenticationManager);
}
#Override
public void configure(HttpSecurity http) throws Exception {
//http configuration
}
}
The TokenStore is just an instance of InMemoryTokenStore and AuthenticationManager is set up this way:
#Configuration
protected static class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
protected UserDetailsManager userManager;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preAuthenticationAuthenticationProvider())
.userDetailsService(userManager).passwordEncoder(PASSWORD_ENCODER);
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Bean
protected AuthenticationProvider preAuthenticationAuthenticationProvider() {
PreAuthenticatedAuthenticationProvider authenticationProvider =
new PreAuthenticatedAuthenticationProvider();
UserDetailsByNameServiceWrapper userDetailsWrapper = new UserDetailsByNameServiceWrapper(userManager);
authenticationProvider.setPreAuthenticatedUserDetailsService(userDetailsWrapper);
return authenticationProvider;
}
}
What I was missing are AuthorizationServiceTokenServices and ResourceServerTokenServices. Both these interfaces are implmented by Spring's DefaultTokenServices.
#Bean
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setAuthenticationManager(authenticationManager);
return tokenServices;
}
In the authorization server configuration (AuthorizationServiceConfigurerAdapter), I have the following setup:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenServices(tokenServices()).authenticationManager(authenticationManager);
}
In the resource server configuration (ResourceServerConfigurerAdapter):
#Autowired
private DefaultTokenServices tokenServices;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenServices);
}
With all these components, my app works without any PreAuthenticationAuthenticationProvider bean defined.
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)