How to use the HttpServletRequest in #Service or #Dao layer to get the user presented credentials? - spring-boot

I have implemented the security layer in my project using Spring Boot. Now,I would like to know how to get the request parameters using HttpServletRequest in #Service or #Dao layer. I tried some way to get the request parameters and I could the username and password but I need to pass that in the Dao. My code:
Security Layer code:
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter{
#Autowired
UserDao userDao;
#Autowired
HttpServletRequest request;
#Autowired
#Qualifier("userDetailsService")
UserDetailsService userDetailsService;
UserDetails userDetails;
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/fonts/**", "/images/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
request.getParameter("username");
http.authorizeRequests().antMatchers("/", "/index.html","/home.html","/static/*","/home/*", "/login.html","/login").permitAll();
http.authorizeRequests().anyRequest().fullyAuthenticated().and().httpBasic().and().csrf().disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().usernameParameter("username").passwordParameter("password").loginProcessingUrl("/login/authenticate").successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).invalidateHttpSession(true);
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
// CSRF tokens handling
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
http.addFilterBefore(tokenProcessingFilter(), RequestFetcher.class);
}
/**
* Configures the authentication manager bean which processes authentication
* requests.
*/
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Dao based authentication
auth.userDetailsService(userDetailsService).passwordEncoder(new Md5PasswordEncoder());
}
private AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedHandler() {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.getWriter().append("Access denied");
response.setStatus(403);
}
};
}
/**
* This is used to hash the password of the user
* when we need to use BCrypt.
*/
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
/**
* This bean is load the user specific data when form login is used.
*/
#Bean
public UserDetailsService userDetailsService() {
return new MyCustomUserDetailsService(userDao);
}
#Bean
public RequestFetcher tokenProcessingFilter() throws Exception {
RequestFetcher tokenProcessingFilter = new RequestFetcher();
tokenProcessingFilter.setAuthenticationManager(authenticationManager());
return tokenProcessingFilter;
}
}
public class RequestFetcher extends UsernamePasswordAuthenticationFilter{
private String userName = "";
private String password = "";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = this.getAsHttpRequest(request);
userName = httpRequest.getParameter("username");
System.out.println("===Username===" +userName);
password = httpRequest.getParameter("password");
System.out.println("===Password===" +password);
chain.doFilter(request, response);
}
private HttpServletRequest getAsHttpRequest(ServletRequest request){
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("Expecting an HTTP request");
}
return (HttpServletRequest) request;
}
public String getUserName(){
return userName;
}
public void setUserName(String userName){
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Could anybody please help me to get the request parameters?

Related

Spring Boot security sign-in 403 Forbidden error question

I am having some issue on my Spring security sign-in. Signup works fine with no error but only sign-in returns 403 forbidden error.
I tried add http.httpBasic() and it returns 401 error.
I have http.csrf().disable() in the SecurityConfig.java but it still doesn't work even it's permitAll() condition. I am stuck in this problem for days :/ I tried every single solution that I googled but nothing worked.
Here is SecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();//cross-origin-resource-sharing
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/api/authentication/**").permitAll()//login and register pre-path
.anyRequest().permitAll();
http.addFilterBefore(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public JwtAuthorizationFilter jwtAuthorizationFilter()
{
return new JwtAuthorizationFilter();
}
#Override
#Bean(BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer()
{
#Override
public void addCorsMappings(CorsRegistry registry)
{
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*");
}
};
}
I think from this jwtAutheorizationFiler.java cause the issue if the Security config is fine:
public class JwtAuthorizationFilter extends OncePerRequestFilter
{
#Autowired
private JwtProvider jwtProvider;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
{
Authentication authentication = jwtProvider.getAuthentication(request);
if (authentication != null && jwtProvider.isTokenValid(request))
{
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
SecurityUtils.java
public class SecurityUtils
{
public static final String ROLE_PREFIX = "ROLE_";
public static final String AUTH_HEADER = "authorization";
public static final String AUTH_TOKEN_HEADER = "Bearer";
public static final String AUTH_TOKEN_PREFIX = AUTH_TOKEN_HEADER + " ";
public static SimpleGrantedAuthority convertToAuthority(String role)
{
String formattedRole = role.startsWith(ROLE_PREFIX) ? role : ROLE_PREFIX + role;
return new SimpleGrantedAuthority(formattedRole);
}
public static String extractAuthTokenFromRequest(HttpServletRequest request)
{
String bearerToken = request.getHeader(AUTH_HEADER);
if(StringUtils.hasLength(bearerToken) && bearerToken.startsWith(AUTH_TOKEN_PREFIX))
{
return bearerToken.substring(7);
}
return null;
}
}
CustomUserDetailService.java :
#Service
public class CustomUserDetailsService implements UserDetailsService
{
private LoginService loginService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
Login login = loginService.findByUsername(username)
.orElseThrow(()-> new UsernameNotFoundException("User not found with username: "+ username));
Set<GrantedAuthority> authorities = Set.of(SecurityUtils.convertToAuthority(login.getRole().name()));
return UserPrincipal.builder()
.login(login)
.id(login.getId())
.username(login.getUsername())
.password(login.getPassword())
.authorities(authorities)
.build();
}
}
AuthenticationController.java
#Autowired
private AuthenticationService authenticationService;
#Autowired
private LoginService loginService;
#Autowired
private JwtRefreshTokenService jwtRefreshTokenService;
#PostMapping("sign-up")//api/authentication/sign-up
public ResponseEntity<?> signUp(#RequestBody Login login)
{
if(loginService.findByUsername(login.getUsername()).isPresent())
{
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
return new ResponseEntity<>(loginService.saveLogin(login), HttpStatus.CREATED);
}
#PostMapping("sign-in")//api/authentication/sign-in
public ResponseEntity<?> signIn(#RequestBody Login login)
{
return new ResponseEntity<>(authenticationService.signInAndReturnJWT(login), HttpStatus.OK);
}
AuthenticationServiceImple.java
#Service
public class AuthenticationServiceImpl implements AuthenticationService
{
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtProvider jwtProvider;
#Autowired
private JwtRefreshTokenService jwtRefreshTokenService;
#Override
public Login signInAndReturnJWT(Login signInRequest)
{
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(signInRequest.getUsername(), signInRequest.getPassword())
);
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
String jwt = jwtProvider.generateToken(userPrincipal);
Login signInUser = userPrincipal.getLogin();
signInUser.setAccessToken(jwt);
signInUser.setRefreshToken(jwtRefreshTokenService.createRefreshToken(signInUser.getId()).getTokenId());
return signInUser;
}
}

DisabledException gets converted to InsufficientAuthenticationException by spring boot

I have override the AuthenticationEntryPoint to return my custom error response. Inside an implementation of UserDetailsService I am passing the enabled field of org.springframework.security.core.userdetails.User as false. So each user is disabled (to test this).
But instead of getting DisabledException I always get InsufficientAuthenticationException : Full authentication is required to access this resource.
So I debugged more and found that AbstractUserDetailsAuthenticationProvider#authenticate is throwing the right exception (DisabledException) but at some place it is getting converted to InsufficientAuthenticationException
SecurityConfig.java
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
#ComponentScan(basePackages = {"com.restapi.security"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
private final JwtConfigProperties jwtConfigProperties;
private final Environment environment;
public SecurityConfig(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, JwtConfigProperties jwtConfigProperties, Environment environment) {
this.userDetailsService = userDetailsService;
this.passwordEncoder = passwordEncoder;
this.jwtConfigProperties = jwtConfigProperties;
this.environment = environment;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Bean
JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
final JwtAuthenticationFilter filter = new JwtAuthenticationFilter(authenticationManager(), jwtConfigProperties, environment);
filter.setFilterProcessesUrl(URN_AUTH + URN_LOGIN);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
#Bean
SimpleUrlAuthenticationSuccessHandler successHandler() {
final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
successHandler.setRedirectStrategy(new NoRedirectStrategy());
return successHandler;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and().authorizeRequests()
.antMatchers(HttpMethod.GET,"** private **")
.authenticated()
.antMatchers(HttpMethod.GET, "**public routes here**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(jwtAuthenticationFilter())
.addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtConfigProperties))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and().formLogin().disable().rememberMe().disable()
.httpBasic().disable()
.logout().disable()
.csrf().disable()
;
}
}
RestAuthenticationEntryPoint.java
#Component(value = "restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException {
String errorMessage;
ErrorCode errorCode;
// the authEx is always of type InsufficientAuthenticationException
if (authEx.getClass().equals(BadCredentialsException.class)) {
errorMessage = "Incorrect username or password";
errorCode = ErrorCode.UN_AUTHORIZED;
} else if (authEx.getClass().equals(DisabledException.class)) {
errorMessage = "Account verification pending, kindly verify your email address. An email has already been sent to your registered email address. If you have trouble finding it, kindly check the spam folder as well.";
errorCode = ErrorCode.DISABLED;
} else {
errorMessage = authEx.getMessage();
errorCode = ErrorCode.UN_AUTHORIZED;
}
ObjectMapper mapper = new ObjectMapper();
ApiErrorDto apiErrorDto = new ApiErrorDto();
apiErrorDto.setTimestamp(System.currentTimeMillis());
apiErrorDto.setStatus(HttpStatus.UNAUTHORIZED.value());
apiErrorDto.setCode(errorCode.value());
apiErrorDto.setType(errorCode.getReasonPhrase());
apiErrorDto.setError(authEx.getClass().getSimpleName());
apiErrorDto.setMessage(errorMessage);
apiErrorDto.setPath(request.getRequestURI());
response.setContentType(APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.println(mapper.writeValueAsString(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(apiErrorDto)));
writer.flush();
}
}
UserDetailsService.java
#Service("userDetailsService")
#Transactional
public class MyUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public MyUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username)
.orElseThrow(() -> new ResourceNotFoundException("No account found for " + username));
return buildAuthenticatedUser(user);
}
private org.springframework.security.core.userdetails.User buildAuthenticatedUser(User user) {
String username = user.getEmail();
String password = user.getPassword();
String uniqueId = user.getUniqueId();
int userId = user.getUserId();
boolean enabled = false; //hardcoded for now
CurrentUser authenticatedUser = new CurrentUser(username, password, enabled, true, true, true, Collections.emptyList());
authenticatedUser.setUniqueId(uniqueId);
authenticatedUser.setUserId(userId);
return authenticatedUser;
}
}
////
#Getter(value = AccessLevel.PACKAGE)
#Setter(value = AccessLevel.PACKAGE)
public class CurrentUser extends User {
private static final long serialVersionUID = -9189146656953053184L;
private Integer userId;
private String uniqueId;
protected CurrentUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
}
What am I doing wrong here? How can I debug it more?
Custom implementation of AuthenticationEntryPoint works well with Basic authentication. But if we write a custom authentication filter we need to handle this unsuccessful login in that filter itself. In my case I am extending UsernamePasswordAuthenticationFilter so here is my implementation for unsuccessful authentication.
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JwtConfigProperties jwtConfigProperties;
private final Environment environment;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtConfigProperties jwtConfigProperties, Environment environment) {
this.authenticationManager = authenticationManager;
this.jwtConfigProperties = jwtConfigProperties;
this.environment = environment;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//logic
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) throws IOException {
String errorMessage;
ErrorCode errorCode;
int httpStatus = HttpStatus.UNAUTHORIZED.value();
if (authEx.getClass().equals(BadCredentialsException.class)) {
errorMessage = "Incorrect username or password";
errorCode = ErrorCode.UN_AUTHORIZED;
} else if (authEx.getClass().equals(DisabledException.class)) {
errorMessage = "Account verification pending, kindly verify your email address. An email has already been sent to your registered email address. If you have trouble finding it, kindly check the spam folder as well.";
errorCode = ErrorCode.DISABLED;
httpStatus = HttpStatus.FORBIDDEN.value();
} else {
errorMessage = authEx.getMessage();
errorCode = ErrorCode.UN_AUTHORIZED;
}
ObjectMapper mapper = new ObjectMapper();
ApiErrorDto apiErrorDto = new ApiErrorDto();
apiErrorDto.setTimestamp(System.currentTimeMillis());
apiErrorDto.setStatus(httpStatus);
apiErrorDto.setCode(errorCode.value());
apiErrorDto.setType(errorCode.getReasonPhrase());
apiErrorDto.setError(authEx.getClass().getSimpleName());
apiErrorDto.setMessage(errorMessage);
apiErrorDto.setPath(request.getRequestURI());
response.setContentType(APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.println(mapper.writeValueAsString(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(apiErrorDto)));
writer.flush();
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) throws IOException, ServletException {
//logic
super.successfulAuthentication(request, response, chain, auth);
}
#Setter
#Getter
private static class LoginCredentials {
private String username;
private String password;
}
}
This now throw the actual exception thrown inside AbstractUserDetailsAuthenticationProvider.
Hope it would help someone.

Spring Security - How to catch Authenticatin Exception from custom Authentication provider

I'm trying the catch and customize Authentication exception thrown in Customized Authentication Filter but once the exception is thrown it always goes to the provider manager class and sends default spring API error response.
WebSecurity Config class
#Configuration
public static class RestAPISecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
AuthenticationEntry authenticationEntryPoint;
#Autowired
BasicAuthenticationProvider customAuthProvider;
#Autowired
AuthenticationFailureHandler accessDeniedHandler;
private final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
new AntPathRequestMatcher(API_PATH_IDENTIFIER));
#Value("${username}")
private String userName;
#Value("${password}")
private String password;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthProvider);
auth.inMemoryAuthentication().withUser("abcd").password(passwordEncoder().encode("sample#123"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/api")
.authenticated().and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(SECURITY_EXCLUDED_PATHS);
}
}
AuthenticationProvider class:
#Component
public class BasicAuthenticationProvider implements AuthenticationProvider {
#Autowired
#Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
#Override
public Authentication authenticate(Authentication auth) throws UserAuthenticationException
{
String username = auth.getName();
String password = auth.getCredentials()
.toString();
AuthenticationException exception = null;
try {
if ("abcd".equals(username) && "Sample#123".equals(password)) {
return new UsernamePasswordAuthenticationToken
(username, password, Collections.emptyList());
} else {
throw new BadCredentialsException("invalid user");
}
}catch(AuthenticationException e)
{
exception = e;
throw exception;
}
}
#Override
public boolean supports(Class<?> auth) {
return auth.equals(UsernamePasswordAuthenticationToken.class);
}
}
AuthenticationEntryPOint class:
/**
*
* Authentication entry point to handle security exceptions
*
*/
#Component
public class AuthenticationEntry implements AuthenticationEntryPoint{
#Autowired
#Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
/**
*This handles security exceptions and redirects it to exception handler
*/
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws UserAuthenticationException {
httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
resolver.resolveException(httpServletRequest, httpServletResponse, null, new UserAuthenticationException("Not Authorized"));
}
}
you can do the exception in the filterChain

Spring Boot Jwt returns access denied

Hey everyone i have problem with jwt with Java.Here is the codes.
Here is returned value from postman
{
"timestamp": "2020-02-29T20:53:35.761+0000",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/login"
}
TokenManager.java
#Service
public class TokenManager {
private static final int expiredAt = 10 * 60 * 60 * 1000;
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
public String generateToken(String username){
return Jwts.builder().setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiredAt))
.signWith(key).compact();
}
public boolean tokenValidate(String token){
if(getUserFromToken(token) != null && isExpired(token)) {
return true;
}
return false;
}
public String getUserFromToken(String token){
Claims claims = getClaims(token);
return claims.getSubject();
}
public boolean isExpired(String token){
Claims claims = getClaims(token);
return claims.getExpiration().after(new Date(System.currentTimeMillis()));
}
private Claims getClaims(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
}
And then JwtTokenFilter.java
#Component
public class JwtTokenFilter extends OncePerRequestFilter {
#Autowired
private TokenManager tokenManager;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
#NotNull HttpServletResponse httpServletResponse,
#NotNull FilterChain filterChain) throws ServletException, IOException {
final String authHeader = httpServletRequest.getHeader("Authorization");
String username = null;
String token = null;
if (authHeader != null && authHeader.contains("Bearer")) {
token = authHeader.substring(7);
try {
username = tokenManager.getUserFromToken(token);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
if (username != null && token != null
&& SecurityContextHolder.getContext().getAuthentication() == null) {
if (tokenManager.tokenValidate(token)) {
UsernamePasswordAuthenticationToken upassToken =
new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
upassToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(upassToken);
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
And my custom UserDetailService
#Service
public class CustomUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}
Here is WebSecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtTokenFilter tokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/signup","/login").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
public AuthenticationManager getAuthenticationManager() throws Exception {
return super.authenticationManagerBean();
}
}
And last one is my controller.I checked the request body and and print the data it just work fine but /login path returns access denied.
#RestController
public class UserController {
#Autowired
private UserService userService;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenManager tokenManager;
public UserController(UserService userService, AuthenticationManager authenticationManager, TokenManager tokenManager) {
this.userService = userService;
this.authenticationManager = authenticationManager;
this.tokenManager = tokenManager;
}
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public ResponseEntity<User> signup(#RequestBody User user){
return ResponseEntity.ok(userService.save(user));
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login(#Valid #RequestBody AuthRequest authRequest){
try{
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authRequest.getUsername(),authRequest.getPassword()));
return ResponseEntity.ok(tokenManager.generateToken(authRequest.getUsername()));
}catch (Exception e){
throw e;
}
}
}
When I remove authenticationManager.authenticate method inside login function it returns a valid token.But when I add authenticationManager again it returns access denied.
Actually you did not setup the AuthenticationManager properly.
in your code, you just used the default authentication manager. And it is ok, as there is one default implementation shipped in Spring boot security, which is ProviderManager. what [ProviderManager][1] does is:
Iterates an Authentication request through a list of AuthenticationProviders.
So you need at least one AuthenticationProvider
There are quite some AuthenticationProviders, for example:
AnonymousAuthenticationProvider, NullAuthenticationProvider, DaoAuthenticationProvider, LdapAuthenticationProvider etc
And in your case, you are authenticating against database, so the DaoAuthenticationProvider is the choice.
And Spring security has a very easy way to configure the DaoAuthenticationProvider, and actually, it automatically created one for you when you set userDetailsService to the AuthenticationManagerBuilder to configure your AuthenticationManager, code like this:
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
so all you need to do is add the code snipet above to your WebSecurityConfig
And it is also recommended to use PasswordEncoder instead of storing your password as plain text. A simple way is to use BCryptPasswordEncoder to encode your password before save the user to db...
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}

403 Forbidden on using JWT Authorization in springboot

I was trying to implement basic authentication and authorization in springboot. But i am getting 403 Forbidden error when i send a get request to an endpoint.I have already added JWT token in Authorization header. I am trying to send a Get request to "/user".
Here's the code which I have written
UserController
#RequestMapping(value = "/user")
#RestController
public class UserController {
#Autowired
UserService userService;
#PostMapping
public UserDetailsResponseModel createUser(#RequestBody UserDetailsRequestModel userRequestObject)
{
UserDetailsResponseModel userResponse = new UserDetailsResponseModel();
UserDTO userDto = new UserDTO();
BeanUtils.copyProperties(userRequestObject,userDto);
UserDTO createdUser = userService.createUser(userDto);
BeanUtils.copyProperties(createdUser,userResponse);
return userResponse;
}
#GetMapping
public String getUser()
{
return "Get user was called";
}
}
WebSecurity Class
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private final UserService userService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserService userService,BCryptPasswordEncoder bCryptPasswordEncoder)
{
this.userService = userService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new AuthenticationFilter(authenticationManager()))
.addFilter(new AuthorizationFilter(authenticationManager()));
}
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
}
}
Authorization Filter
public class AuthorizationFilter extends BasicAuthenticationFilter {
public AuthorizationFilter(AuthenticationManager authenticationManager)
{
super(authenticationManager);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(SecurityConstants.HEADER_STRING);
if(header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX));
{
chain.doFilter(request,response);
}
UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request,response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(SecurityConstants.HEADER_STRING);
token = token.replace(SecurityConstants.TOKEN_PREFIX,"");
String user = Jwts.parser()
.setSigningKey(SecurityConstants.TOKEN_SECRET)
.parseClaimsJws(token)
.getBody()
.getSubject();
if(user != null){
return new UsernamePasswordAuthenticationToken(user,null,new ArrayList<>());
}
return null;
}
}
Authentication Filter
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public AuthenticationFilter(AuthenticationManager authenticationManager)
{
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
UserLoginRequestModel creds = new ObjectMapper().readValue(request.getInputStream(),UserLoginRequestModel.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getEmail(),
creds.getPassword(),
new ArrayList<>()
)
);
}
catch (IOException ex){
throw new RuntimeException(ex);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
String userName = ((User) authResult.getPrincipal()).getUsername();
String token = Jwts.builder()
.setSubject(userName)
.setExpiration(new Date(System.currentTimeMillis()+SecurityConstants.EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512,SecurityConstants.TOKEN_SECRET)
.compact();
UserService userService = (UserService) SpringApplicationContext.getBean("userServiceImplementation");
UserDTO userDTO = userService.getUser(userName);
response.addHeader(SecurityConstants.HEADER_STRING,SecurityConstants.TOKEN_PREFIX+token);
response.addHeader("UserID",userDTO.getUserId());
}
}

Resources