how to implement api key secure in spring boot? - java

I saw this code for apikey in spring boot, but how to apply this secure when an application tries use my api service?
What is the next step for use this security and disable the basic autentication user password of spring?
package com.microservice.reportGenerator.validation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
#Configuration
#EnableWebSecurity
#Order(1)
public class APISecurityConfig extends WebSecurityConfigurerAdapter {
// "${yourapp.http.auth-token-header-name}"
#Value("${ReportService.http.auth-token-header-name}")
private String principalRequestHeader;
#Value("${ReportService.http.auth-token}")
private String principalRequestValue;
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
APIKeyAuthFilter filter = new APIKeyAuthFilter(principalRequestHeader);
System.out.println(principalRequestHeader + " Y" + principalRequestValue);
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;
}
});
httpSecurity.antMatcher("/**").csrf().disable().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().addFilter(filter).authorizeRequests()
.anyRequest().authenticated();
}
}
package com.microservice.reportGenerator.validation;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
private String principalRequestHeader;
public APIKeyAuthFilter(String principalRequestHeader) {
this.principalRequestHeader = principalRequestHeader;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
System.out.println(request.getHeader(principalRequestHeader));
return request.getHeader(principalRequestHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}

Related

Parameter 0 of constructor in AuthService required a bean of type 'springframework.security.crypto.password.PasswordEncoder' that could not be found

Below you will find the 2 classes that I think you guys need. If any more info is required please let me know.
AuthService
package com.r.reservationproject.services;
import com.r.reservationproject.dto.AuthenticationResponse;
import com.r.reservationproject.dto.RegisterRequest;
import com.r.reservationproject.domain.NotificationEmail;
import com.r.reservationproject.domain.User;
import com.r.reservationproject.domain.VerificationToken;
import com.r.reservationproject.dto.LoginRequest;
import com.r.reservationproject.exceptions.SpringReservationAccountException;
import com.r.reservationproject.repositories.UserRepo;
import com.r.reservationproject.repositories.VerificationTokenRepo;
import com.r.reservationproject.security.JwtProvider;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotBlank;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
#Service
#AllArgsConstructor
#Transactional
// Contains logic to create username password authentication token and use authenticationManager to perform login
public class AuthService {
private final PasswordEncoder passwordEncoder;
private final UserRepo userRepo;
private final VerificationTokenRepo verificationTokenRepo;
private final MailService mailService;
private final AuthenticationManager authenticationManager;
private final JwtProvider jwtProvider;
public void signup(RegisterRequest registerRequest) {
User user = new User();
user.setUsername(registerRequest.getUsername());
user.setEmail(registerRequest.getEmail());
user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
user.setCreated(Instant.now());
user.setEnabled(false);
userRepo.save(user);
String token = generateVerificationToken(user);
mailService.sendMail(new NotificationEmail("Activeren account applicatie", user.getEmail(),
"Bedankt voor het gebruiken van onze applicatie, om uw account te activeren vragen wij u om volgende link te klikken :" +
"http://localhost:8080/api/auth/accountVerification/" + token));
}
private String generateVerificationToken(User user) {
String token = UUID.randomUUID().toString();
VerificationToken verificationToken = new VerificationToken();
verificationToken.setToken(token);
verificationToken.setUser(user);
verificationTokenRepo.save(verificationToken);
return token;
}
public void verifyAccount(String token) {
Optional<VerificationToken> verificationToken = verificationTokenRepo.findByToken(token);
verificationToken.orElseThrow(() -> new SpringReservationAccountException("Token is niet geldig"));
fetchUserAndEnable(verificationToken.get());
}
public void fetchUserAndEnable(VerificationToken verificationToken) {
#NotBlank(message = "Username is required") String username = verificationToken.getUser().getUsername();
User user = userRepo.findByUsername(username).orElseThrow(() -> new SpringReservationAccountException("Gebruiker onbekend"));
user.setEnabled(true);
userRepo.save(user);
}
public AuthenticationResponse login(LoginRequest loginRequest) {
Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authenticate);
String token = jwtProvider.generateToken(authenticate);
// to send this token we use a dto (authenticationresponse)
return new AuthenticationResponse(token, loginRequest.getUsername());
}
}
SecurityConfig
package com.r.reservationproject.config;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#EnableWebSecurity
#AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/api/auth/**")
.permitAll()
.anyRequest()
.authenticated();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
}
Full error:
APPLICATION FAILED TO START
Description:
Parameter 0 of constructor in com.r.reservationproject.services.AuthService required a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' in your configuration.
I don't get the error since I already defined the Bean in the SecurityConfig
I appreciate your help a lot!
Try adding #Configuration annotation in your SecurityConfig class

Spring AuthenticationManager and Circular Dependencies Problem on SecurityConfig

I'm learning about security config in a tutorial and will go to the JWT Authentication material. I have a problem with circular dependencies error in AuthenticationManager. I've checked everything but I can't see what I'm doing wrong. I hope you guys can help me with the problem I'm having.
..>bookcatalog>config>SecurityConfig.class
package com.bluedev.bookcatalog.config;
import com.bluedev.bookcatalog.security.filter.UsernamePasswordAuthenticationFilter;
import com.bluedev.bookcatalog.security.provider.UsernamePasswordAuthProvider;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String V1_URL = "/v1/**";
private static final String V2_URL = "/v2/**";
#Autowired
private AuthenticationSuccessHandler successHandler;
#Autowired
private AuthenticationFailureHandler failureHandler;
#Autowired
private UsernamePasswordAuthProvider usernamePasswordAuthProvider;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private ObjectMapper objectMapper;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.userDetailsService(appUserService).passwordEncoder(appConfig.passwordEncoder());
auth.authenticationProvider(usernamePasswordAuthProvider);
}
protected UsernamePasswordAuthenticationFilter buildUsernamePasswordAuthFilter(String loginEntryPoint){
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter(loginEntryPoint, successHandler, failureHandler, objectMapper);
filter.setAuthenticationManager(authenticationManager);
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(V1_URL, V2_URL).authenticated()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(buildUsernamePasswordAuthFilter("/v1/login"), org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class);
}
}
..>bookcatalog>security>filter>UsernamePasswordAuthenticationFilter.java
package com.bluedev.bookcatalog.security.filter;
import com.bluedev.bookcatalog.dto.LoginRequestDTO;
import com.bluedev.bookcatalog.exception.BadRequestException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final AuthenticationSuccessHandler successHandler;
private final AuthenticationFailureHandler failureHandler;
private final ObjectMapper objectMapper;
public UsernamePasswordAuthenticationFilter(String defaultFilterProcessesUrl, AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler, ObjectMapper objectMapper) {
super(defaultFilterProcessesUrl);
this.successHandler = successHandler;
this.failureHandler = failureHandler;
this.objectMapper = objectMapper;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
LoginRequestDTO dto = objectMapper.readValue(request.getReader(), LoginRequestDTO.class);
if(StringUtils.isBlank(dto.getUsername()) || StringUtils.isBlank(dto.getPassword())){
throw new BadRequestException("username & password must be provided.");
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(dto.getUsername(), dto.getPassword());
return this.getAuthenticationManager().authenticate(token);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
this.successHandler.onAuthenticationSuccess(request, response, authResult);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
this.failureHandler.onAuthenticationFailure(request, response, failed);
}
}
..>bookcatalog>security>provider>UsernamePasswordAuthProvider.java
package com.bluedev.bookcatalog.security.provider;
import com.bluedev.bookcatalog.service.AppUserService;
import lombok.AllArgsConstructor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
#AllArgsConstructor
#Component
public class UsernamePasswordAuthProvider implements AuthenticationProvider {
private final AppUserService appUserService;
private final PasswordEncoder passwordEncoder;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
UserDetails appUser = appUserService.loadUserByUsername(username);
if(!passwordEncoder.matches(password, appUser.getPassword())){
throw new BadCredentialsException("invalid.username.password");
}
return new UsernamePasswordAuthenticationToken(appUser, null, appUser.getAuthorities());
}
#Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
..>bookcatalog/security/handler/UsernamePasswordAuthSuccessHandler.java
package com.bluedev.bookcatalog.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
#AllArgsConstructor
#Component
public class UsernamePasswordAuthSuccessHandler implements AuthenticationSuccessHandler {
private final ObjectMapper objectMapper;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String, String> resultMap = new HashMap<>();
resultMap.put("result", "SUCCESS");
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
objectMapper.writeValue(response.getWriter(), resultMap);
}
}
..>bookcatalog/security/handler/UsernamePasswordAuthFailureHandler.java
package com.bluedev.bookcatalog.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
#AllArgsConstructor
#Component
public class UsernamePasswordAuthFailureHandler implements AuthenticationFailureHandler {
private final ObjectMapper objectMapper;
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
Map<String, String> resultMap = new HashMap<>();
resultMap.put("result", "FAILURE");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
objectMapper.writeValue(response.getWriter(), resultMap);
}
}
Error Result
2022-06-17 18:31:06.226 ERROR 18092 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌──->──┐
| securityConfig (field private org.springframework.security.authentication.AuthenticationManager com.bluedev.bookcatalog.config.SecurityConfig.authenticationManager)
└──<-──┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
Process finished with exit code 1
Remove below line from SecurityConfig
#Autowired
private AuthenticationManager authenticationManager;
And modify the buildUsernamePasswordAuthFilter function to use authenticationManagerBean() function
protected UsernamePasswordAuthenticationFilter buildUsernamePasswordAuthFilter(String loginEntryPoint){
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter(loginEntryPoint, successHandler, failureHandler, objectMapper);
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}

Postman gives 401 Unauthorized -"spring boot security"

I'm trying to configure Spring Security on a Spring Boot application as follows and as a result Postman gives 401 Unauthorized ,please can anyone help me to solve this error
controller
package com.example.demo.controller;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
#CrossOrigin(origins = "http://localhost:8080")
#RestController
#RequestMapping("/api")
public class UserController {
#Autowired
private UserService userService;
#PostConstruct
public void initRoleAndUser() {
userService.initRoleAndUser();
}
#PostMapping({"/registerNewUser"})
public User registerNewUser(#RequestBody User user) {
return userService.registerNewUser(user);
}
#GetMapping({"/forAdmin"})
#PreAuthorize("hasRole('Admin')")
public String forAdmin(){
return "This URL is only accessible to the admin";
}
#GetMapping({"/forUser"})
#PreAuthorize("hasRole('User')")
public String forUser(){
return "This URL is only accessible to the user";
}
}
configuration
package configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
private UserDetailsService jwtService;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors();
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/authenticate", "/registerNewUser").permitAll()
.antMatchers(HttpHeaders.ALLOW).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(jwtService).passwordEncoder(passwordEncoder());
}
}
JwtService
package com.example.demo.service;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.demo.dao.UserDao;
import com.example.demo.Util.JwtUtil;
import com.example.demo.entity.User;
import com.example.demo.entity.JwtResponse;
import com.example.demo.entity.JwtRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.HashSet;
import java.util.Set;
#Service
public class JwtService implements UserDetailsService {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private UserDao userDao;
private AuthenticationManager authenticationManager;
public JwtResponse createJwtToken(JwtRequest jwtRequest) throws Exception {
String userName = jwtRequest.getName();
String userPassword = jwtRequest.getUser_code();
authenticate(userName, userPassword);
UserDetails userDetails = loadUserByUsername(userName);
String newGeneratedToken = jwtUtil.generateToken(userDetails);
User user = userDao.findById(userName).get();
return new JwtResponse(user, newGeneratedToken);
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findById(username).get();
if (user != null) {
return new org.springframework.security.core.userdetails.User(
user.getName(),
user.getUser_code(),
getAuthority(user)
);
} else {
throw new UsernameNotFoundException("User not found with username: " + username);
}
}
private Set getAuthority(User user) {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
user.getRoles().forEach(role -> {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
});
return authorities;
}
private void authenticate(String userName, String userPassword) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, userPassword));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED");
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS");
}
}
}
I use postman to test and when I do a GET to "http://localhost:8080/forAdmin", I get an error 401 Unauthorized
You must load the user into memory from the database, and it should be converted to UserDetails. The user details has Roles, or Authorities. It dependes what you choose. Maybe these articles can help :)
https://www.baeldung.com/spring-security-authentication-with-a-database
https://www.baeldung.com/role-and-privilege-for-spring-security-registration

Field jwtFilter required a bean of type 'filter.JwtFilter' that could not be found.?

I want to start my spring app after i made some jwt authentification configuration. but i have this issues :
Description:
Field jwtFilter in com.soheibKehal.CashApi.config.SecurityConfig required a bean of type 'filter.JwtFilter' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'filter.JwtFilter' in your configuration.
The thing is in my SecurityConfig class, i already Beaned my AuthentificationManager method, so i dont know what is the problem ?
package com.soheibKehal.CashApi.config;
import com.soheibKehal.CashApi.services.CustomUserDetailService;
import filter.JwtFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailService userDetailsService;
#Autowired
private JwtFilter jwtFilter;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/authenticate")
.permitAll().anyRequest().authenticated()
.and().exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);;
}
}
my jwtFilter class :
package filter;
import com.soheibKehal.CashApi.Util.JwtUtil;
import com.soheibKehal.CashApi.services.CustomUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
public class JwtFilter extends OncePerRequestFilter {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private CustomUserDetailService service;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException, IOException, ServletException {
String authorizationHeader = httpServletRequest.getHeader("Authorization");
String token = null;
String userName = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
token = authorizationHeader.substring(7);
userName = jwtUtil.extractUsername(token);
}
if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = service.loadUserByUsername(userName);
if (jwtUtil.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
Do you have any idea what is the problem ?
I think the problem is in your package structure. Springboot will scan your app for autoconfiguration starting from the package where you have a class of this structure
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
It should be in the package com.soheibKehal.CashApi.
The solution will be to move the filter package into the com.soheibKehal.CashApi just beside your config package.
Then spring will be able to locate the JwtFilter for autoconfiguration.

Spring Boot: error="invalid_grant", error_description="Bad credentials"

I wrote a very Spring boot simple app.
To get the authentication token, I used the following curl command. But instead I see the following error in the server log (eclipse console): error="invalid_grant", error_description="Bad credentials"
curl -v -u greetings:123456 -X POST http://localhost:8080/oauth/token -H "Accept: application/json" -d "username=username&password=password&grant_type=password&scope=write&client_secret=12345&client_id=greetings"
I wonder:
What have I done wrong that it doesn't let me get the auth code?
OAuth2ServerConfiguration.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
#Configuration
#EnableResourceServer
#EnableAuthorizationServer
class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
String applicationName = "greetings";
// This is required for password grants, which we specify below as one of the
// {#literal authorizedGrantTypes()}.
#Autowired
AuthenticationManagerBuilder authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// Workaround for https://github.com/spring-projects/spring-boot/issues/1801
endpoints.authenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
// TODO Auto-generated method stub
return authenticationManager.getOrBuild().authenticate(authentication);
}
});
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(applicationName)
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
.authorities("ROLE_USER")
.scopes("write")
.resourceIds(applicationName)
.secret("123456");
}
}
WebSecurityConfiguration.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.User;
import com.boot.myproj.repository.AccountRepository;
class WebSecurityConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Autowired
AccountRepository userRepository;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Bean
UserDetailsService userDetailsService() {
return (username) -> userRepository
.findByUsername(username)
.map(a -> new User(a.username, a.password, true, true, true, true,
AuthorityUtils.createAuthorityList("USER", "write")))
.orElseThrow(
() -> new UsernameNotFoundException("could not find the user '"
+ username + "'"));
}
}
Account.java
import javax.persistence.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
#Entity
public class Account {
#Id
#GeneratedValue
private Long id;
public Long getId() {
return id;
}
public String getPassword() {
return password;
}
public String getUsername() {
return username;
}
#JsonIgnore
public String password;
public String username;
public Account(String name, String password) {
this.username = name;
this.password = password;
}
Account() { // jpa only
}
}
AccountRepository.java
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import com.boot.myproj.config.security.Account;
public interface AccountRepository extends JpaRepository<Account, String>{
public Optional<Account> findByUsername(String username);
}
App.java
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.boot.cut_costs.config.security.Account;
import com.boot.cut_costs.repository.AccountRepository;
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Bean
CommandLineRunner init(AccountRepository accountRepository) {
return (arg) -> {
accountRepository.save(new Account("username", "password"));
};
}
}
Add #Configuration to WebSecurityConfiguration class. So that your bean will get injected in spring context.
p.s. What you are trying to do here with OAuth2 password grant will not return an auth code , instead it'll directly return you a accessToken.

Categories

Resources