I'm trying to learn Java and Spring, i've been following a tutorial step by step about spring security, but i'm getting the http response forbidden, again i'm a rookie in Java so if it's a silly mistake, bealive me i can't see it.
here's my SpringSecurityConfig.java
package com.bolsadeideasspringboot.app;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.User;
import org.springframework.security.core.userdetails.User.UserBuilder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.bolsadeideasspringboot.app.auth.filter.JWTAuthenticationFilter;
import com.bolsadeideasspringboot.app.auth.filter.JWTAuthorizationFilter;
import com.bolsadeideasspringboot.app.auth.handler.LoginSuccessHandler;
import com.bolsadeideasspringboot.app.models.service.JpaUserDetailService;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private LoginSuccessHandler successHandler;
#Autowired
private DataSource dataSource;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private JpaUserDetailService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/css/**", "/js/**", "/img/**", "/listar**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Autowired
public void configurerglobal(AuthenticationManagerBuilder build) throws Exception {
build.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
}
Here's my JWTAuthenticationFilter.java
package com.bolsadeideasspringboot.app.auth.filter;
import java.io.Console;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.bolsadeideasspringboot.app.models.entity.Usuario;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authManager;
public JWTAuthenticationFilter(AuthenticationManager authManager) {
this.authManager = authManager;
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/login", "POST"));
//setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/cliente/listar", "POST"));
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// TODO Auto-generated method stub
String username = obtainUsername(request);
String password = obtainPassword(request);
if(username != null && password != null) {
System.out.println("");
}
else {
Usuario user = null;
try {
user = new ObjectMapper().readValue(request.getInputStream(), Usuario.class);
username = user.getUsername();
password = user.getPassword();
} catch (JsonParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username,password);
username = username.trim();
return authManager.authenticate(authToken);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
// TODO Auto-generated method stub
Collection<? extends GrantedAuthority> roles = authResult.getAuthorities();
Claims claims = Jwts.claims();
claims.put("authorities", new ObjectMapper().writeValueAsString(roles));
String token = Jwts.builder()
.setClaims(claims)
.setSubject(authResult.getName())
.signWith(SignatureAlgorithm.HS512, "Clave!#$%&/()Secreta.//1234.4321.//.abcd.dcba.1a.2b.3c.4d.12345678".getBytes())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000L))
.compact();
response.addHeader("Authorization", "Bearer"+token);
Map<String, Object> body = new HashMap<String, Object>();
body.put("token", token);
body.put("user", (User)authResult.getPrincipal());
body.put("mensaje", "Inicio de sesión correcto");
response.getWriter().write(new ObjectMapper().writeValueAsString(body));
response.setStatus(200);
response.setContentType("application/json");
//super.successfulAuthentication(request, response, chain, authResult);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
// TODO Auto-generated method stub
Map<String, Object> body = new HashMap<String, Object>();
body.put("mensaje","Error de Autenticación, Claves de Acceso incorrectas");
body.put("Error", failed.getMessage());
response.getWriter().write(new ObjectMapper().writeValueAsString(body));
response.setStatus(401);
response.setContentType("application/json");
}
}
Here's the JWTAuthorizationFilter.java
package com.bolsadeideasspringboot.app.auth.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import com.bolsadeideasspringboot.app.auth.SimpleGrantedAuthoritiesMixin;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
// TODO Auto-generated constructor stub
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String header = request.getHeader("Authorization");
if(!requiresAuthentication(header)) {
chain.doFilter(request, response);
return;
}
boolean tokenValid;
Claims token = null;
try {
token = Jwts.parser()
.setSigningKey("Clave!#$%&/()Secreta.//1234.4321.//.abcd.dcba.1a.2b.3c.4d.12345678".getBytes())
.parseClaimsJws(header.replace("Bearer ", "")).getBody();
tokenValid = true;
}
catch(JwtException e) {
tokenValid = false;
}
UsernamePasswordAuthenticationToken authentication = null;
if(tokenValid) {
String username = token.getSubject();
Object roles = token.get("authorities");
Collection<? extends GrantedAuthority> authorities = Arrays.asList(new ObjectMapper()
.addMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthoritiesMixin.class)
.readValue(roles.toString().getBytes(), SimpleGrantedAuthority[].class));
authentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
protected boolean requiresAuthentication(String header) {
if (header == null || !header.toLowerCase().startsWith("Bearer ")) {
return false;
}
return true;
}
}
So, basically, when i try to log in in the login route ("http://127.0.0.1:8080/api/login"), it works, the app generates the token
but, when i try to send the credentials to the ("http://127.0.0.1:8080/api/cliente/listar"), i get the 403 error every single time
Any help would be appreciated, once again, i'm a rookie in java, so please bear with me, thanks.
Related
I followed a tutorial on Spring Security. I have set up an authentication that works: here is my authentication filter.
package com.securitycrud.crudTest.filter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class CustomAuthentificationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public CustomAuthentificationFilter(AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
return authenticationManager.authenticate(authenticationToken);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
User user = (User)authentication.getPrincipal();
Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
String access_token = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis()+ 10 * 60 * 1000))
.withIssuer(request.getRequestURL().toString())
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining()))
.sign(algorithm);
String refresh_token = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis()+ 30 * 60 * 1000))
.withIssuer(request.getRequestURL().toString())
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining()))
.sign(algorithm);
Map<String, String> tokens = new HashMap<>();
tokens.put("access_token", access_token);
tokens.put("refresh_token", refresh_token);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);
}
}
Then here is the authorization and config class.
package com.securitycrud.crudTest.security;
import com.securitycrud.crudTest.filter.CustomAuthentificationFilter;
import com.securitycrud.crudTest.filter.CustomAuthorizationFilter;
import lombok.RequiredArgsConstructor;
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
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.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
#Configuration #EnableWebSecurity #RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
CustomAuthentificationFilter customAuthentificationFilter = new CustomAuthentificationFilter(authenticationManagerBean());
customAuthentificationFilter.setFilterProcessesUrl("/api/login");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(STATELESS);
// en dessous: les url qu'on ne veut pas sécuriser par l'authentification: ex page d'acceuil
http.authorizeRequests().antMatchers("/api/login", "/api/token/refresh").permitAll();
http.authorizeRequests().antMatchers("/api/users").hasAnyRole("ROLE_ADMIN","ROLE_USER");
http.authorizeRequests().antMatchers(POST, "/api/user/save/**").hasAnyAuthority("ROLE_ADMIN");
http.authorizeRequests().anyRequest().authenticated();
http.addFilter(customAuthentificationFilter);
http.addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
}
package com.securitycrud.crudTest.filter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static java.util.Arrays.stream;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class CustomAuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(request.getServletPath().equals("/api/login")||request.getServletPath().equals("/api/token/refresh") ){
filterChain.doFilter(request,response);
} else {
String authorizationHeader = request.getHeader(AUTHORIZATION);
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")){
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
try {
filterChain.doFilter(request, response);
} catch (IOException | ServletException e) {
throw new RuntimeException(e);
}
});
}catch (Exception exception){
log.error("error loggin in: {}", exception.getMessage());
response.setHeader("error", exception.getMessage());
response.setStatus(FORBIDDEN.value());
Map<String, String> error = new HashMap<>();
error.put("error_token", exception.getMessage());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
} else {
filterChain.doFilter(request, response);
}
}
}
}
Nevertheless, when I try on Postman, I connect well, and I get the token well (and I am role user)
Then, I test the GET api/users request.
And there I can't. I get the following error:
403 Forbidden: {
"error_token": "The input is not a valid base 64 encoded string."
}
I don't understand.... I tried to search but no way...
Does anyone have an idea?
' Hello,
I have 2 problems with this code now.
In auth Controller, the JWT token generator is empty just showing this {}. It should show
{"username ": "string " , "password" : "string" }
Once is registered through auth controller and checked in Database in Role it is generating a row and id =0 and name= null but it suppose to assign either Normal/Admin right. Also when I try to register 2nd time it gets an error as Internal server error, please help me with this. '
package com.mohammedkhadeer.blog.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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.mohammedkhadeer.blog.exceptions.ApiException;
import com.mohammedkhadeer.blog.payloads.JwtAuthRequest;
import com.mohammedkhadeer.blog.payloads.JwtAuthResponse;
import com.mohammedkhadeer.blog.payloads.UserDto;
import com.mohammedkhadeer.blog.security.JwtTokenHelper;
import com.mohammedkhadeer.blog.services.UserService;
#RestController
#RequestMapping("/api/v1/auth/")
public class AuthController {
#Autowired
private JwtTokenHelper jwtTokenHelper;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserService userService;
#PostMapping("/login")
public ResponseEntity<JwtAuthResponse> createToken(#RequestBody JwtAuthRequest request) throws Exception {
this.authenticate(request.getUsername(), request.getPassword());
UserDetails userDetails = this.userDetailsService.loadUserByUsername(request.getUsername());
String token = this.jwtTokenHelper.generateToken(userDetails);
JwtAuthResponse response = new JwtAuthResponse();
response.setToken(token);
return new ResponseEntity<JwtAuthResponse>(response, HttpStatus.OK);
}
private void authenticate(String username, String password) throws Exception {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,
password);
try {
this.authenticationManager.authenticate(authenticationToken);
} catch (BadCredentialsException e) {
System.out.println("Invalid Detials !!");
throw new ApiException("Invalid username or password !!");
}
}
// register new user api
#PostMapping("/register")
public ResponseEntity<UserDto> registerUser(#RequestBody UserDto userDto) {
UserDto registeredUser = this.userService.registerNewUser(userDto);
return new ResponseEntity<UserDto>(registeredUser, HttpStatus.CREATED);
}
}
package com.mohammedkhadeer.blog.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenHelper jwtTokenHelper;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 1. get token
String requestToken = request.getHeader("Authorization");
// Bearer 2352523sdgsg
System.out.println(requestToken);
String username = null;
String token = null;
if (requestToken != null && requestToken.startsWith("Bearer")) {
token = requestToken.substring(7);
try {
username = this.jwtTokenHelper.getUsernameFromToken(token);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get Jwt token");
} catch (ExpiredJwtException e) {
System.out.println("Jwt token has expired");
} catch (MalformedJwtException e) {
System.out.println("invalid jwt");
}
} else {
System.out.println("Jwt token does not begin with Bearer");
}
// once we get the token , now validate
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (this.jwtTokenHelper.validateToken(token, userDetails)) {
// shi chal rha hai
// authentication karna hai
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
} else {
System.out.println("Invalid jwt token");
}
} else {
System.out.println("username is null or context is not null");
}
filterChain.doFilter(request, response);
}
}
package com.mohammedkhadeer.blog;
import java.util.List;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.mohammedkhadeer.blog.config.AppConstants;
import com.mohammedkhadeer.blog.entities.Role;
import com.mohammedkhadeer.blog.repositories.RoleRepo;
#SpringBootApplication
public class BlogAppApisApplication implements CommandLineRunner {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private RoleRepo roleRepo;
public static void main(String[] args) {
SpringApplication.run(BlogAppApisApplication.class, args);
}
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
#Override
public void run(String... args) throws Exception {
System.out.println(this.passwordEncoder.encode("xyz"));
try {
Role role = new Role();
role.setId(AppConstants.ADMIN_USER);
role.setName("ROLE_ADMIN");
Role role1 = new Role();
role1.setId(AppConstants.NORMAL_USER);
role1.setName("ROLE_NORMAL");
List<Role> roles = List.of(role, role1);
List<Role> result = this.roleRepo.saveAll(roles);
result.forEach(r -> {
System.out.println(r.getName());
});
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
The title is self explanatory, I have this API made in Spring, and after JWT implementation I can login, but the gets doesn't work anymore.
But if I comment the the CorsConfigurationSource corsConfigurationSource() and the Lombok annotation #RequiredArgsConstructor in SecurityConfig, the login stop to work, but all the gets work beautifully.
I'll post the class SecurityConfig, AuthorizationFilter and the AuthenticationFilter respectively for you guys try to see whats going wrong.
import org.alterdata.shopback.app.security.AuthenticationFilter;
import org.alterdata.shopback.app.security.AuthorizationFilter;
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.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.web.authentication.UsernamePasswordAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
UserDetailsService userDetailsService;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManagerBean());
http.cors().and().csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/login").permitAll()
.antMatchers("/cadastrar/**").hasAnyAuthority("ADMIN")
.antMatchers("/cadastro/**").hasAnyAuthority("ADMIN")
.anyRequest().authenticated();
http.addFilter(authenticationFilter);
http.addFilterBefore(new AuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManager()throws Exception{
return super.authenticationManager();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE","OPTIONS", "HEAD"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.auth0.jwt.exceptions.JWTVerificationException;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.bytebuddy.implementation.bind.annotation.IgnoreForBinding.Verifier;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class AuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if(request.getServletPath().equals("/login")) {
filterChain.doFilter(request, response);
}else {
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256("segredinho".getBytes());
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
String user = decodedJWT.getSubject();
String [] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
Arrays.stream(roles).forEach(role ->{
authorities.add(new SimpleGrantedAuthority(role));
});
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user,null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}catch(Exception e){
log.error("erro ao realizar o login! {}", e.getMessage());
response.setHeader("erro", e.getMessage());
response.setStatus(401);
Map<String, String> error = new HashMap<>();
error.put("mensagem de erro", e.getMessage());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
}else {
filterChain.doFilter(request, response);
}
}
}
}
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.web.bind.annotation.CrossOrigin;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
#CrossOrigin(origins = {"*"})
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{
String nome = request.getParameter("nome");
String senha = request.getParameter("senha");
UsernamePasswordAuthenticationToken uspsToken = new UsernamePasswordAuthenticationToken(nome, senha);
//response.setHeader("teste", String.valueOf(uspsToken));
return authenticationManager.authenticate(uspsToken);
}catch (Exception e){
throw new RuntimeException();
}
}
// #Override
// public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
// throws AuthenticationException {
// try{
// UserPasswordAuthRequest userPasswordAuthRequest = new ObjectMapper()
// .readValue(request.getInputStream(), UserPasswordAuthRequest.class);
//
// Authentication authentication = new UsernamePasswordAuthenticationToken(
// userPasswordAuthRequest.getNome(),
// userPasswordAuthRequest.getSenha()
// );
// return authenticationManager.authenticate(authentication);
// }catch (IOException e){
// throw new RuntimeException();
// }
// }
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
User user = (User) authResult.getPrincipal();
Algorithm algorithm = Algorithm.HMAC256("segredinho".getBytes());
String tokenAcesso = JWT.create().withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 10*60*1000*60))
.withIssuer(request.getRequestURL()
.toString()).withClaim("roles", user.getAuthorities()
.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
String tokenRefresh = JWT.create().withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 10*60*1000*60))
.withIssuer(request.getRequestURL()
.toString()).withClaim("roles", user.getAuthorities()
.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
Map<String, String> tokens = new HashMap<>();
tokens.put("tokenacesso", tokenAcesso);
tokens.put("tokenrefresh", tokenRefresh);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(),tokens);
}
}
First try to call your api with Postman, else i think you need to add authorization jwt header to every api request after you login.
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
return { Authorization: 'Bearer ' + user.accessToken };
} else {
return {};
}
}
and then call your api like this:
return axios.get(API_URL + 'user', { headers: authHeader() });
for more informations try to visit: https://www.bezkoder.com/react-jwt-auth/
I just fixed using this global cors
#Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
I'm trying to implement JWT authentication in Spring Security. But I don't understand how can I use it without UserDetails from DB, because I get users from remote LDAP server and don't store them in my DB.
How I can replace this fetching UserDetails for another one thing? Any ideas?
Here my JwtAuthenticationFilter class:
package com.kddb_webimporter.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
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.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
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 com.kddb_webimporter.models.User;
import java.io.IOException;
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
// #Autowired
private JwtTokenProvider tokenProvider;
// #Autowired
private CustomUserDetailsService customUserDetailsService;
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
public JwtAuthenticationFilter(
AuthenticationManager authenticationManager,
JwtTokenProvider tokenProvider,
CustomUserDetailsService customUserDetailsService ) {
super(authenticationManager);
this.tokenProvider = tokenProvider;
this.customUserDetailsService = customUserDetailsService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
UserDetails userDetails = customUserDetailsService.loadUserByUsername("");
/*How to replace it?*/
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
I am trying to setup authentication using JWT using Angular as my front end and Spring as backend. I have the following AuthenticationFilter class for setting up JWT filter:
package com.atlas.config;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
public class AuthenticationFilter implements Filter {
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//System.out.println("filter");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String authHeader = ((HttpServletRequest) request).getHeader("Authorizatoin");
//System.out.println(authHeader);
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new ServletException("Missing or invalid Authorization header.");
}
String token = authHeader.substring(7);
try
{
//security key needs to be changed before deployment
Claims claims = Jwts.parser().setSigningKey("feb1atlaskey").parseClaimsJws(token).getBody();
req.setAttribute("claims", claims);
}
catch(SignatureException e)
{
throw new ServletException("Not a valid token");
}
chain.doFilter(req,res);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
This is my UserController class where the login check and setting Auth header happens:
package com.atlas.controller;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.atlas.entity.User;
import com.atlas.service.UserService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#RestController
#RequestMapping("/users")
public class UserController {
private final Map<String, List<String>> userDb = new HashMap<>();
public UserController() {
userDb.put("tom", Arrays.asList("user"));
userDb.put("sally", Arrays.asList("user", "admin"));
}
#RequestMapping(value = "login", method = RequestMethod.POST)
public LoginResponse login(#RequestBody final UserLogin login)
throws ServletException {
System.out.println("In user controller");
if (login.name == null || !userDb.containsKey(login.name)) {
throw new ServletException("Invalid login");
}
return new LoginResponse(Jwts.builder().setSubject(login.name)
.claim("roles", userDb.get(login.name)).setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "secretkey").compact());
}
#Autowired
private UserService userService;
#RequestMapping(method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public void addUser(#RequestBody User u) {
this.userService.addUser(u);
}
private static class UserLogin {
public String name;
public String password;
}
private static class LoginResponse {
public String token;
public LoginResponse(final String token) {
this.token = token;
}
}
}
Now, when I do a login using simple form and make a POST request using Angular, its not reaching UserController. Its hitting the filter and throwing Servlet Exception as "Missing or invalid Authorization header".
I have also configured my app to add this filter in the chain as below:
package com.atlas.config;
import javax.servlet.Filter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInit extends AbstractAnnotationConfigDispatcherServletInitializer{
#Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return new Class[]{AppConfig.class,SwaggerConfig.class,AuthenticationFilter.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
#Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[] {"/api/*"};
}
#Override
protected Filter[] getServletFilters() {
Filter [] singleton = { new CORSFilter(), new AuthenticationFilter() };
return singleton;
}
}
Am I missing some config here or making some terrible mistake.
Any help much appreciated!
The "Authorization" header is mispelled:
Replace from:
String authHeader = ((HttpServletRequest) request).getHeader("Authorizatoin");
To:
String authHeader = ((HttpServletRequest) request).getHeader("Authorization");
You may consider using Spring Framework constants for http headers to avoid such issues:
String authHeader = ((HttpServletRequest) request).getHeader(org.springframework.http.HttpHeaders.AUTHORIZATION);