spring security - Home page setup for authorize and unauthorize user - spring-boot

i'm stuck in spring security configuration. can any one help me for better solution. i have 2 jsp page one for login user and other for simple user. in which login user have option for logout and while in other jsp have option for login and signup.
my secuirty configuration class
#Configuration
#EnableWebSecurity
public class SecureConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Value("${winni.auth.exit}")
private String authExit;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().requestMatchers()
.antMatchers("/login","/web/**" ,"/exit","/action/**","/cart/**","/cart/xhr/**","/buyer/**")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll().and()
.logout().logoutSuccessUrl(authExit);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**","/account/**","/start/**");
}
}
home controller is
#RequestMapping("/")
public String sayHello(Principal principal) {
if (principal != null) {
return "login_user";
} else {
return "simple_user";
}
}
in every case Principal Object also null. how can solve this issue.

Related

spring boot API + security +ldap

I want to achieve LDAP verification without form login, But not sure how to achieve it.
EDITED: I have a custom POST API /login which accept userId and password and validate it and creates a session cookies/Token.
Few doubts:
how to point to custom login URL endpoint?
How internally the form login used to validate the cookie once we successfully validated the credentials? How to achieve the same with custom login endpoint?
Do i have to change something in CRSF?
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LdapProperties ldapProperties;
#Bean
public LdapAuthenticationProvider
authenticationProvider(final LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
#Bean
public BindAuthenticator authenticator(final BaseLdapPathContextSource contextSource) {
final BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserDnPatterns(new String[] {
ldapProperties.getUserDnPatterns() });
return authenticator;
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userSearchFilter(ldapProperties.getSearchFilter())
.contextSource(contextSource());
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.formLogin().and()
.authorizeRequests()
.anyRequest().fullyAuthenticated().and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login");
}
#Override
public void configure(final WebSecurity web) throws Exception {
web.debug(true);
}
#Bean
public LdapContextSource contextSource() {
final LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(ldapProperties.getUrl());
contextSource.setBase(ldapProperties.getBase());
contextSource.setUserDn(ldapProperties.getUserDn());
contextSource.setPassword(ldapProperties.getUserDnPaswrd());
contextSource.setPooled(true);
contextSource.afterPropertiesSet();
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate() {
final LdapTemplate ldapTemplate = new LdapTemplate(
contextSource());
ldapTemplate.setIgnorePartialResultException(true);
return ldapTemplate;
}
}

Spring Security Permitting even unauthenticated user? whats wrong with this code?

I doing a simple poc of spring security but am running in to issues.
After extending WebSecurityConfigurerAdapter and implementing own UserDetailsService, spring security is permitting all users even without authentication. Please point the flaw in below code
...
#EnableWebSecurity
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService myUserDtlSrv;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDtlSrv);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
my implementation of UserDetailsService
...
#Service
public class MyUserDtlSrv implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User("a", "a", Arrays.asList());
}
}
Does not spring security secure every url (resource by default)?
You need to specify what requests are authorized. The following fragment
authorizes authenticated (by form login) requests.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.csrf(t -> t.disable())
.authorizeRequests(t -> t.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
}
}

Connect multiple authentication mechanisms Spring Boot Security

I have a security configuration for my application that authenticates the user via LDAP. This works out pretty fine, but now I'd like to add another AuthenticationProvider that does some more checks on the user that tries authenticate. So I tried to add a DbAuthenticationProvider that (for testing purposes) always denies the access. So when I am trying to log in with my domain account (that works for the activeDirectoryLdapAuthenticationProvider) I am not able to access the page because the second provider fails the authentication.
To accomplish this goal, I used the following code:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${ad.domain}")
private String AD_DOMAIN;
#Value("${ad.url}")
private String AD_URL;
#Autowired
UserRoleComponent userRoleComponent;
#Autowired
DbAuthenticationProvider dbAuthenticationProvider;
private final Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);
#Override
protected void configure(HttpSecurity http) throws Exception {
this.logger.info("Verify logging level");
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin()
.successHandler(new CustomAuthenticationSuccessHandler()).and().httpBasic().and().logout()
.logoutUrl("/logout").invalidateHttpSession(true).deleteCookies("JSESSIONID");
http.formLogin().defaultSuccessUrl("/", true);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
auth.authenticationProvider(dbAuthenticationProvider);
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider(), dbAuthenticationProvider));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN,
AD_URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
And this is my DbAuthenticationProvider:
#Component
public class DbAuthenticationProvider implements AuthenticationProvider {
Logger logger = LoggerFactory.getLogger(DbAuthenticationProvider.class);
#Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
auth.setAuthenticated(false);
this.logger.info("Got initialized");
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return true;
}
}
Sadly I am able to log in (the access is not denied as I expected it to be). Did I miss out something?
Spring Won't use more than one AuthenticationProvider to authenticate the request, so the first (in the ArrayList) AuthenticationProvider that support the Authentication object and successfully authenticate the request will be the only one used. in your case it's activeDirectoryLdapAuthenticationProvider.
instead of using ActiveDirectoryLdapAuthenticationProvider, you can use a custom AuthenticationProvider that delegates to LDAP and do additional checks:
CustomerAuthenticationProvider implements AuthenticationProvider{
privtae ActiveDirectoryLdapAuthenticationProvider delegate; // add additional methods to initialize delegate during your configuration
#Override
public Authentication authenticate(Authentication auth) throws
AuthenticationException {
Authentication authentication= delegate.authenticate(auth);
additionalChecks(authentication);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
public void additionalCheck(Authentication authentication){
// throw AuthenticationException when it's not allowed
}
}
That is not how an AuthenticationProvider works, only one will be consulted for authentication. Apparently you want to combine some information from LDAP and from the DB. For this you can configure a custom UserDetailsContextMapper and/or GrantedAuthoritiesMapper. The default implementation will use the information from LDAP to contruct the UserDetails and its GrantedAuthorities however you could implement a strategy which consults the database.
Another solution is to use the LdapUserDetailsService which allows you to use the regular DaoAuthenticationProvider. The name is misleading as it actually requires an UserDetailsService. This AuthenticationProvider does additional checks using the UserDetailsChecker, which by default checks some of the properties on the UserDetails, but can be extended with your additional checks.
NOTE: The LdapUserDetailsService uses plain LDAP so I don't know if that is applicable to the slightly different Active Directory approach!
A final solution could be to create a DelegatingAuthenticationProvider which extends from AbstractUserDetailsAuthenticationProvider so that you can reuse the logic in there to utilize the UserDetailsChecker. The retrieveUser method would then delegate to the actual ActiveDirectoryLdapAuthenticationProvider to do the authentication.
NOTE: Instead of extending the AbstractUserDetailsAuthenticationProvider you could of course also create a simpler version yourself.
All in all I suspect that creating a customized UserDetailsContextMapper would be the easiest and when not found in DB throw an UsernameNotFoundException. This way the normal flow still applies and you can reuse most of the existing infrastructure.
As sample work around on multiple authentication mechanism :
find the code
#Configuration
#EnableWebSecurity
#Profile("container")
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
private AuthenticationProvider authenticationProviderDB;
#Override
#Order(1)
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Order(2)
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProviderDB);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/scripts/**","/styles/**","/images/**","/error/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/**").authenticated()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(new AuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication a) throws IOException, ServletException {
//To change body of generated methods,
response.setStatus(HttpServletResponse.SC_OK);
}
})
.failureHandler(new AuthenticationFailureHandler() {
#Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
})
.loginProcessingUrl("/access/login")
.and()
.logout()
.logoutUrl("/access/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
#Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication a) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
})
.invalidateHttpSession(true)
.and()
.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and()
.csrf()//Disabled CSRF protection
.disable();
}
}
configured two authentication providers in Spring Security
<security:authentication-manager>
<security:authentication-provider ref="AuthenticationProvider " />
<security:authentication-provider ref="dbAuthenticationProvider" />
</security:authentication-manager>
configuration which helps configure multiple authentication providers in java config.
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.authenticationProvider(DBauthenticationProvider);
}
#Configuration
#EnableWebSecurity
public class XSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LDAPAuthenticationProvider authenticationProvider;
#Autowired
private DBAuthenticationProvider dbauthenticationProvider;
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/scripts/**","/styles/**","/images/**","/error/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.authenticationProvider(dbauthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/","/logout").permitAll()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/index")
.loginProcessingUrl("/perform_login")
.usernameParameter("user")
.passwordParameter("password")
.failureUrl("/index?failed=true")
.defaultSuccessUrl("/test",true)
.permitAll()
.and()
.logout().logoutUrl("/logout")
.logoutSuccessUrl("/index?logout=true").permitAll()
.and()
.exceptionHandling().accessDeniedPage("/error");
}
}
objectPostProcessor inside the configure method need AuthenticationManagerBuilder to actually build the object before we can access and change the order of the providers
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.passwordEncoder(new BCryptPasswordEncoder());
auth.authenticationProvider(new CustomAuthenticationProvider(this.dataSource));
auth.objectPostProcessor(new ObjectPostProcessor<Object>() {
#Override
public <O> O postProcess(O object) {
ProviderManager providerManager = (ProviderManager) object;
Collections.swap(providerManager.getProviders(), 0, 1);
return object;
}
});
}

There is no PasswordEncoder mapped for the id "null" in spring basic security

my code is working fine in spring version 1.5.6.RELEASE.
But when i upgrade version to 2.0.0, some of my methods have been deprecated but works fine.
when i change my deprecated method by looking from There is no PasswordEncoder mapped for the id "null" with database authentication
it starts giving me the error "there is no passwordencoder"
Here is my code.
WebConfig
#Configurable
#EnableWebSecurity
public class WebConfig extends WebSecurityConfigurerAdapter {
#Autowired
AppUserDetailsService appUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(appUserDetailsService);
}
#Bean
public static DelegatingPasswordEncoder passwordEncoder() {
return (DelegatingPasswordEncoder) DelegatingPasswordEncoder.encode("noop");
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(appUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#SuppressWarnings("deprecation")
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:4200");
}
};
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.authorizeRequests()
.antMatchers("/account/register","/account/login","/logout").permitAll()
.anyRequest().fullyAuthenticated().and()
.logout()
.permitAll()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "POST"))
.and()
.httpBasic().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).and()
.csrf().disable();
}
}
Account Controller
#RestController
#RequestMapping("account")
public class AccountController {
public static final Logger logger = LoggerFactory.getLogger(AccountController.class);
#Autowired
private UserService userService;
#CrossOrigin
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> createUser(#RequestBody User newUser) {
if (userService.find(newUser.getUsername()) != null) {
logger.error("username Already exist " + newUser.getUsername());
return new ResponseEntity(
new CustomErrorType("user with username " + newUser.getUsername() + "already exist "),
HttpStatus.CONFLICT);
}
newUser.setRole("USER");
return new ResponseEntity<User>(userService.save(newUser), HttpStatus.CREATED);
}
#CrossOrigin
#RequestMapping("/login")
public Principal user(Principal principal) {
logger.info("user logged "+principal);
return principal;
}
}
User Service
#Service
public class UserService {
#Autowired
UserRepository userRepository;
public User save(User user) {
return userRepository.saveAndFlush(user);
}
public User update(User user) {
return userRepository.save(user);
}
public User find(String userName) {
return userRepository.findOneByUsername(userName);
}
}
I see all the related answers but they are available for inMemory authentication.
Spring security : migrating 4.0 to 5.0 - Error -There is no PasswordEncoder mapped for the id “null”
Spring Security 5 : There is no PasswordEncoder mapped for the id "null"
Spring Boot PasswordEncoder Error
Please help.
I'm using mysql database.
From the documentation :
NoOpPasswordEncoder : A password encoder that does nothing. Useful
for testing where working with plain text passwords may be preferred.
They were deprecated because they aren't secured. As i see, if you still want to use "noop" password encoder, just build your own password encoder implementation.
https://github.com/spring-projects/spring-security/blob/master/crypto/src/main/java/org/springframework/security/crypto/password/NoOpPasswordEncoder.java
Hope this helps.

Spring OAuth2 "Full authentication is required to access this resource error" when trying to access login url

I am using Spring Security OAuth2 (Spring Boot 2.0.2 + Spring Cloud Finchley) and trying to initiate an implicit login. The browser redirects me to the /login URL but I get the the error "Full authentication is required to access this resource." How do I allow the login page to be displayed but still allow all REST urls to be secured?
My config is as follows:
App.java
#SpringBootApplication
#RestController
#EnableResourceServer
#EnableAuthorizationServer
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
OAuth2Config.java
#Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("XXXXX")
.secret("XXXXX")
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
WebSecurityConfigurer.java
#Configuration
#Order(-20) // EDIT
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("XXXXX"). password("XXXXXX").roles("USER");
}
// EDIT
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().permitAll()
.and().httpBasic().and()
.requestMatchers()
//specify urls handled
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.antMatchers("/fonts/**", "/js/**", "/css/**")
.and()
.authorizeRequests()
.antMatchers("/fonts/**", "/js/**", "/css/**").permitAll()
.anyRequest().authenticated();
}
}
}

Resources