Spring Security Custom Authentication Provider 403 response - java

I'm trying to implement a simple custom authentication provider with Spring Security in a Spring Boot App, but its not working.
My Custom Auth provider is:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider{
#Override
public Authentication authenticate(Authentication a) {
try{
List<GrantedAuthority> roles = new ArrayList<>();
roles.add(new SimpleGrantedAuthority("USER"));
UsernamePasswordAuthenticationToken u = new UsernamePasswordAuthenticationToken("usuario", "password", roles);
return u;
}catch(Exception e){
return null;
}
}
#Override
public boolean supports(Class<?> type) {
return true;
}
}
And my Security config is this:
#Configuration
#EnableWebSecurity
#EntityScan(basePackages = "sirio.io.models")
public class AppConfiguration {
#Configuration
#Order(1)
public static class ApiWebSecurity extends WebSecurityConfigurerAdapter{
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Override
public void configure(HttpSecurity http) throws Exception{
http.antMatcher("/admin/**")
.authorizeRequests()
.anyRequest()
.hasRole("USER")
.and()
.httpBasic()
.and()
.authenticationProvider(customAuthenticationProvider);
}
}
}
I have settled several breakpoints in the CustomAuthProvider and it have been called but i always get a 403 response in the browser
[EDIT]
Tried another type of Custom Auth Provider but with same result.
#Component
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider{
#Override
protected void additionalAuthenticationChecks(UserDetails ud, UsernamePasswordAuthenticationToken upat) throws AuthenticationException {
}
#Override
protected UserDetails retrieveUser(String string, UsernamePasswordAuthenticationToken upat) throws AuthenticationException{
List<GrantedAuthority> authoritys = new ArrayList<>();
authoritys.add(new SimpleGrantedAuthority("USER"));
UserDetails ud = new User("usuario", "password", authoritys);
return ud;
}
}

Spring has an abstract implementation for username/password authentication. Extend it to your required implementation.
https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java

Related

Spring boot securing api with both basic authorization and jwt role based authorization

I am trying to secure my api requests with both basic authorization and jwt role based authorization.
I have two classes for basic auth and web security config. Both jwt role based auth and basic auth classed are imported in WebSecurityConfigurerAdapter.
When running the application, api is working only with basic auth and does not know jwt token included or not.
WebSecurityConfigurerAdapter class
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
final private static String REALM = "UWAPP_SECURITY_REALM";
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Autowired
public WebSecurityConfig(UserDetailsServiceImpl userDetailsService) {
super();
this.userDetailsService = userDetailsService;
}
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.authorizeRequests()
.antMatchers("/actuator/health", "/api/auth/signup", "/api/auth/login", "/api/auth/logout").permitAll()
.antMatchers("/api/test/public").permitAll()
.antMatchers("/api/test/user").hasAnyAuthority(UserLoginRole.USER.value())
.anyRequest().authenticated()
.and()
.formLogin().disable()
.csrf().disable()
.httpBasic().realmName(REALM)
.authenticationEntryPoint(getBasicAuthEntryPoint());
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.cors();
}
#Bean
public BasicAuthentication getBasicAuthEntryPoint() {
return new BasicAuthentication();
}
}
BasicAuthentication class
public class BasicAuthentication extends BasicAuthenticationEntryPoint {
final private static String REALM = "UWAPP_SECURITY_REALM";
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authException) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName() + "");
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 : " + authException.getMessage());
}
#Override
public void afterPropertiesSet() {
setRealmName(REALM);
super.afterPropertiesSet();
}
}
Requests did not pass through authorization jwt token included or not.
What am I missing here?

How to get logged user id in REST API

I have built a Java application with the REST API convention. I working on endpoint which returns objects only if object is connected with user by common id in database(ManyToOne annotation). In order to achieve that i need current logged user id for comapring it with object's user id. If Ids are the same, endpoint returns data. I know solutions as "Principal" or "Authentication" classes but they provide everything except of "id". I used spring security http basic for authentication.
My authentication classes:
#Component
public class CustomAuthenticator implements AuthenticationProvider {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
#Autowired
public CustomAuthenticator(UserRepository userRepository, #Lazy PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String login = authentication.getName();
String password = authentication.getCredentials().toString();
User user = userRepository.findByLogin(login).orElseThrow(() -> new EntityNotFoundException("User not found"));
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new BadCredentialsException("Bad credentials");
}
return new UsernamePasswordAuthenticationToken(login, password, convertAuthorities(user.getRoles()));
}
private Set<GrantedAuthority> convertAuthorities(Set<UserRole> userRoles) {
Set<GrantedAuthority> authorities = new HashSet<>();
for (UserRole ur : userRoles) {
authorities.add(new SimpleGrantedAuthority(ur.getRole().toString()));
}
return authorities;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
SECURITY CONFIG CLASS:
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomAuthenticator customAuthenticator;
public SecurityConfig(CustomAuthenticator customAuthenticator) {
this.customAuthenticator = customAuthenticator;
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
return passwordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api").permitAll()
.antMatchers("/api/register").permitAll()
//TODO everybody now has access to database, change it later
.antMatchers("/h2-console/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
http
.csrf().disable()
.headers().frameOptions().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticator);
}
}
Does someone know how to resolve that problem ?
You can use UserDetails class and set id for the username field, this class provides by spring security.
If you don't want that solution, you can create a Subclass extend UserDetails class and decide an id field. When receiving the request, parse principal to UserDetails or subclass extends UserDetails to get the id
Ex:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetails userPrincipal = (UserDetails)authentication.getPrincipal();

Execute multi provider even the previous one executes successfully in WebSecurityConfigurerAdapter

I'm new with spring and I'm setting up Authentication in my application. I need to execute two providers if and only if the first executes successfully.
The problem: When the first provider is authenticated, the second not execute. I'm using a ActiveDirectoryLdapAuthenticationProvider first, then I need to execute a CustomProvider.
That is my code:
#Configuration
#EnableWebSecurity
public class AdWebSecurity extends WebSecurityConfigurerAdapter {
...
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
//ActiveDirectoryLdapAuthenticationProvider
auth.authenticationProvider(AdAuthConfig.getLdapProvider());
//CustomProvider
auth.authenticationProvider(AdAuthConfig.getDbProvider());
}
}
//LDAP Provider
public static ActiveDirectoryLdapAuthenticationProvider getLdapProvider()
{
ActiveDirectoryLdapAuthenticationProvider adProvider =
new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN, AD_URL);
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setUseAuthenticationRequestCredentials(true);
adProvider.setSearchFilter(USER_PATTERN);
adProvider.setUserDetailsContextMapper(userDetailsContextMapper());
return adProvider;
}
//CustomAuthenticationProvider
#Component
public class CustomAuthenticationProvider
implements AuthenticationProvider {
#Autowired
private AuthenticationService service;
#Override
public Authentication authenticate( Authentication authentication ) throws AuthenticationException {
String password = authentication.getCredentials().toString();
//AdUserDetails is a custom UserDetail
AdUserDetails userDetails = (AdUserDetails) authentication.getPrincipal();
boolean authenticated =
service.authenticateEmployeeNumber(userDetails.getEmployeeNumber());
authentication.setAuthenticated(authenticated);
if(!authenticated) {
throw new BadCredentialsException("Employee Number(" +
userDetails.getEmployeeNumber() +
"not found!");
}
return new UsernamePasswordAuthenticationToken(userDetails, password);
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Thanks for ur time.

Not being able to provide custom authentication provider for the spring security

I want to have a custom Authentication Provider for spring security and i have implemented it like this
#Component
public class ApiCustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
System.out.println("ahsgdvjasdhgjasjdh");
return new UsernamePasswordAuthenticationToken("aman", "12345");
}
#Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
Right now i don't have any logic as i just want to see if spring security is actually using this authentication provider .
i have my security config file as
#Configuration
#EnableWebSecurity
//#ImportResource("classpath:/security/spring_saml_sso_security.xml")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/*#Autowired
MetadataGeneratorFilter metadataGeneratorFilter;
#Autowired
FilterChainProxy samlFilter;
#Autowired
SAMLEntryPoint samlEntryPoint;
*/
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
try {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/static/**").permitAll()
.antMatchers("/settings/api/**").permitAll()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.loginProcessingUrl("/login")
// .usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("/index",true)
.and()
.httpBasic();
// .defaultSuccessUrl("/", true);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("sadhiasdniaaaaaaaaaaaaaaaa:");
e.printStackTrace();
}
}
#Bean
public ApiCustomAuthenticationProvider apiCustomAuthenticationProvider() {
return new ApiCustomAuthenticationProvider();
}
}
i want to know if this
#Bean
public ApiCustomAuthenticationProvider apiCustomAuthenticationProvider() {
return new ApiCustomAuthenticationProvider();
is the correct way of telling spring security to use the custom authentication manager .
You need to add this in Spring security config:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new ApiCustomAuthenticationProvider());
}
or
auth.authenticationProvider(apiCustomAuthenticationProvider())
And as a reminder, if you return token :
UsernamePasswordAuthenticationToken("aman", "12345"),
spring will not give authorization to user. Instead you need to assign role :
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
UsernamePasswordAuthenticationToken("aman", "12345",grantedAuths) ;
As stated above,you are giving user ROLE_USER and then user can use all authenticated page.
Hope its help.

Spring security custom login url

I have the following Sprring web app:
#Secured({"ROLE_ADMIN"})
#RequestMapping(value = "data/{id}", method = RequestMethod.GET)
public Object getData(#RequestPath String id)
#RequestMapping(value = "login", method = RequestMethod.GET)
public Object login(#RequestParam String username, #RequestParam String password)
In login I need to call another server, pass credentials and get back roles, then let spring know to use these roles for incoming user.
After login client can use getData method if pass authorization of ROLE_ADMIN.
How can I implement this behaviour using java config?
UPDATE:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public AuthenticationProvider authenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private static final Logger logger = LogFactory.getLogger();
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
log.debug("name=" + name + " password=" + password);
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
logger.debug("supports authentication=" + authentication);
return true;
}
}
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
But as I can see from the logs CustomAuthenticationProvider.authenticate is never called.
Did I miss something?
Thanks.
UPDATE 2: correct solution for me:
Remove Login url from authentication config
add exception handler to disable redirection in case of authentication error
add success handler to send user valid json response
use http POST for app/login
#EnableGlobalMethodSecurity(securedEnabled = true) in web config in order to allow #Secured annotation in controller.
Thanks for all prompts.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
**.anyRequest().authenticated()**
.and().formLogin()
.loginProcessingUrl("/login").usernameParameter("username")
.passwordParameter("password")
**.successHandler(authenticationSuccessHandler)**.failureHandler(authenticationFailureHandler)
.and().csrf().disable().**exceptionHandling()
.authenticationEntryPoint(errorsAuthenticationEntryPoint)**;
}
You need to use WebSecurityConfigurerAdapter like this:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.logoutUrl("/myurl/logout")
.and()
.formLogin()
.loginPage("/myurl/login")
.defaultSuccessUrl("/myurl/login?success");
}
}
Every thing is explain in the documentation https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-form
You will need to implement a custom AuthenticationProvider. Something like:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider());
}
#Bean
AuthenticationProvider customAuthenticationProvider() {
CustomAuthenticationProvider impl = new CustomAuthenticationProvider();
impl.setUserDetailsService(customUserDetailsService());
/* other properties etc */
return impl ;
}
#Bean
UserDetailsService customUserDetailsService() {
/* custom UserDetailsService code here */
}
}

Categories