How to integrate spring security with custom authentication provider - java

I am using third party authenticator, which is invoked when the application starts and shows login screen and after successful authentication, grants access to the application.
After successful authentication it forwards to a spring controller for user loading.
I would like to know how can i use spring security api for custom providers.
I want to call spring security api after the authentication is done and while the user details are being loaded etc to build up roles and GrantedAuthority etcs.
Controller
#Controller
public class WelcomeController {
#RequestMapping(value = { "/welcome" }, method = RequestMethod.POST)
public ModelAndView defaultPage() {
ModelAndView model = new ModelAndView();
//... load user profile from third party authenticator.
return model;
}
}
Security Config
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyCustomAuthenticationProvider thirdPartyAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**")
.access("hasRole('ROLE_ADMIN')")
.and().exceptionHandling().accessDeniedPage("/error.html");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(thirdPartyAuthenticationProvider);
}
}
Custom Provider
public class MyCustomAuthenticationProvider implements AuthenticationProvider{
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// use the credentials to try to authenticate against the third party system
if (authenticatedAgainstThirdPartySystem()) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
return new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
} else {
throw new AuthenticationException("Unable to auth against third party systems");
}
}
#Override
public boolean supports(Class<?> authentication) {
/return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Any Suggestions?

Related

Spring Security Switch between API Key and Basic Auth

I need to configure the Spring Security so that users that have either Basic Auth or API Key can access the API in the absence of one other. The below code works for Basic Auth but when I switch to API Key in Postman, I can not access the API using the correct API key/value added to the Header in Postman in the Authorization tab and I am getting 401.
However, when I just use ApiKeyWebSecurityConfigurerAdapter by itself, I can access the API even with the wrong API key/value pair. Any help would be appreciated.
Note: I used this and https://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/html5/#multiple-httpsecurity as reference.
Security Config:
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.userDetailsService(userDetailsService())
.httpBasic(Customizer.withDefaults());
}
#Bean
public UserDetailsService userDetailsService() {
return new JpaUserDetailService();
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
#Configuration
public static class ApiKeyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Value("${myApp.http.auth-token-header-name}")
private String principalRequestHeader;
#Value("${myApp.http.auth-token}")
private String principalRequestValue;
#Override
protected void configure(HttpSecurity http) {
http
.csrf()
.disable();
APIKeyAuthFilter filter = new APIKeyAuthFilter(principalRequestHeader);
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!principalRequestValue.equals(principal)) {
throw new BadCredentialsException("The API key was not found or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
}
});
}
}
}
Filter:
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
private String principalRequestHeader;
public APIKeyAuthFilter(String principalRequestHeader) {
this.principalRequestHeader = principalRequestHeader;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(principalRequestHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}

Two authentication services in a single application

I have an application that authenticates the login against the Microsoft Active Directory, but now I need that if the user is not in the AD of the organization, try to authenticate against an OpenLDAP directory, is it possible with spring-boot in a single application?
How can I indicate in the configuration class that there are two providers to authenticate? Or, do I have to use a handler or similar to perform double authentication?
My code is similar to the following with its own filters and some changes, but the scheme is similar.
Code source: https://medium.com/#dmarko484/spring-boot-active-directory-authentication-5ea04969f220
#Configuration
#EnableWebSecurity
public class WebSecurityConfigAD extends WebSecurityConfigurerAdapter {
#Value("${ad.domain}")
private String AD_DOMAIN;
#Value("${ad.url}")
private String AD_URL;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN, AD_URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
I am also doing the same in my application:
Authenticate against the LDAP
Authenticate against the database.
First of all LDAP server is already set up.
So in application.properties file we have all the information required to connect to the LDAP server and a Boolean field to check whether we want to authenticate against LDAP server or not.
project.ldap.server.protocol=
project.ldap.server.host.name=
project.ldap.server.ip=
project.ldap.server.port=
project.ldap.service.url=
project.ldap.authentication=(false/true)
A service which will perform the authentication against the LDAP server
#Service
public class LDAPAuthenticationConnectorService {
#Value("${project.ldap.server.protocol}")
private String LDAP_SERVER_PROTOCOL;
#Value("${project.ldap.server.ip}")
private String LDAP_SERVER_IP;
#Value("${project.ldap.server.port}")
private int LDAP_SERVER_PORT;
#Value("${project.ldap.service.url}")
private String LDAP_SERVICE_URL;
/**
*
* #param loginDto
* #throws ADAuthenticationException
*/
public String authenticate(LoginDto loginDto) throws ADAuthenticationException{//logic }
Now in FacadeImplementation you can do as following:
public class LoginFacadeImpl implements LoginFacadeInt {
#Value("${project.ldap.authentication}")
private Boolean isProjectLDAPAuthenticationEnabled;
#Override
public UserDto authenticateUser(String userName, String password) {
try {
authenticateViaLDAP(userName, password);
} catch (Exception e1) {
//do authetication against database
UserEntity userEntityForAuthentication =
UserManagementDaoInt.authenticateUser(userName,
AuthenticationUtils.convertPasswordToSha256(password));
}
Hope this helps:)
Let me know:)
I found the solution.
Spring Security supports a wide range of authentication mechanisms. AuthenticationManagerBuilder object allows using multiple built-in authentication provider like In-Memory authentication, LDAP authentication, JDBC based authentication. In addition to its own set of authentication models, Spring Security allows to write your custom authentication mechanism to authenticate, for example, against a secure RESTful or SOAP remote API authentication service.
Link: https://www.baeldung.com/spring-security-multiple-auth-providers
The following example shows how to put two authentication providers, one in a row from another. One: in memory, and other: customAuthentcationProvider.
auth.authenticationProvider(customAuthProvider);
auth.inMemoryAuthentication()
Example:
#EnableWebSecurity
public class MultipleAuthProvidersSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationProvider customAuthProvider;
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(customAuthProvider);
auth.inMemoryAuthentication()
.withUser("memuser")
.password(encoder().encode("pass"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/api/**")
.authenticated();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

How to add custom filter to HttpSecurity and retrieve the user? (Spring Security)

In WebSecurityConfigurerAdapter class we have protected void configure(HttpSecurity http) method where we add restrictions to request. What i want is:
1.Retrieve a user who wants to enter
2.Check if that user is present in db
3.Check if that user has a special propery, for example.: login.endsWith('_login')
All you need to implement Custom AuthenticationProvider. Do the authentication code by other server as you need.
#Component
public class CustomAuthenticationProvider
implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if (shouldAuthenticateAgainstThirdPartySystem()) {
// use the credentials
// and authenticate against the third-party system
return new UsernamePasswordAuthenticationToken(
name, password, new ArrayList<>());
} else {
return null;
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(
UsernamePasswordAuthenticationToken.class);
}
}
And register your Authentication provider.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
}
}
Note : I am assuming you're using HTTP Basic Authentication, you can to
change it to FormLogin or as per your need.

UserDetailsService is never called(Spring boot security)

I have this SpringSecurity config
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl detailsService;
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/index").access("hasRole('USER')");
http.userDetailsService(detailsService);
}
}
when I open index.html page I get 403 error. It is because user is anonymous. But I want check user and set role and detail before opened this page. For this i write this service
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private HttpServletRequest request;
#Autowired
AuthService authService;
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
String ipAddress = request.getRemoteAddr();
AuthLkUser authLkUserByIp = authService.getAuthLkUserByIp(ipAddress);
if (authLkUserByIp == null) return null;
boolean b = authService.checkAuthLkUser(authLkUserByIp);
if (b) return null;
Set<GrantedAuthority> roles = new HashSet();
roles.add(new SimpleGrantedAuthority("USER"));
UserDetails userDetails = new org.springframework.security.core.userdetails.User(authLkUserByIp.getMsisdn(),
authLkUserByIp.getSubsId(), roles);
return userDetails;
}
}
But this service is never called.
In Spring Boot if you have a custom Spring Security Configuration then it will not use Auto Configuration for Spring Security.
In your Spring security configuration you haven't given anyway to authenticate a user. There are form based, basic header based, etc authentication available in Spring Security. You should use one of that to authenticate user so that during authentication your UserDetailsServiceImpl will be used to load the user.
Follow this post for form based login

Spring Security is not recognizing user roles

It looks very strange behavior, probably a bug.
I have a Spring boot 1.3.2 as backend API using Rest Service and I have another application using Angular 2 consuming these services.
All the security stuff is working ok with JWT Token, I can restricted my services for logged users, I can check the user logged and so on. Authorization is not working 100%, If I add on my services #Secured or #PreAuthorize with some user role this work with swagger and with my MockMvc tests using #WithMockUser(roles="ROLE_TEST") so it's configured OK.
The problem is that Authorization with #Secured or #PreAuthorize is not working when I'm accessing via angular application, All my requests that has #Secured or #PreAuthorize I receive Status 403.
authentication.getAuthorities() that all my roles is being loaded perfectly
Controller:
#RequestMapping(method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
#Secured("ROLE_MANTER_INSTITUICAO")
public List<HierarquiaInstituicao> getAll() {
return service.findAll();
}
Security Config:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled=true, jsr250Enabled=true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private UserService userService;
private final TokenAuthenticationService tokenAuthenticationService;
public SpringSecurityConfig() {
super(true);
this.userService = new UserService();
tokenAuthenticationService = new TokenAuthenticationService(userService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().and().anonymous().and().servletApi().and()
.authorizeRequests()
// Allow anonymous logins
.antMatchers("/api/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(userService).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;
}
}
My filter:
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final TokenAuthenticationService authenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
Authentication authentication = authenticationService.getAuthentication(httpRequest);
if(authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
else {
httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
}
SecurityContextHolder.getContext().setAuthentication(null);
}
}

Categories

Resources