I have multiple clients registered for my oauth2 auth server. lets say user1 have roles such as ROLE_A, ROLE_B for client1, same user has roes such as ROLE_C, ROLE_D for client2. now when the user logins either using client1 or client2 he is able to see all the four roles ie. ROLE_A, ROLE_B, ROLE_C and ROLE_D.
My requirement was when the user1 logins to client1 it should return only the roles ROLE_A and ROLE_B. when he logins using client2 it should return only ROLE_C and ROLE_D
For achieving this, what I planned is within the authenticate function, I need to get the clientId. so using the clientId and the username I can find the corresponding roles allocated to the user from the db (client-user-roles-mapping table). .But the issue is I don't know how to get the clientId within the authenticate function
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String userName = ((String) authentication.getPrincipal()).toLowerCase();
String password = (String) authentication.getCredentials();
if (userName != null && authentication.getCredentials() != null) {
String clientId = // HERE HOW TO GET THE CLIENT ID
Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
Authentication token = new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
return token;
} else {
throw new BadCredentialsException("Authentication Failed!!!");
}
} else {
throw new BadCredentialsException("Username or Password cannot be empty!!!");
}
}
Can anyone please help me on this
UPDATE 1
CustomAuthenticationProvider.java
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
private LDAPAuthenticationProvider ldapAuthentication;
#Autowired
private AuthRepository authRepository;
public CustomAuthenticationProvider() {
super();
}
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String userName = ((String) authentication.getPrincipal()).toLowerCase();
String password = (String) authentication.getCredentials();
if (userName != null && authentication.getCredentials() != null) {
String clientId = // HERE HOW TO GET THE CLIENT ID
Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
Authentication token = new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
return token;
} else {
throw new BadCredentialsException("Authentication Failed!!!");
}
} else {
throw new BadCredentialsException("Username or Password cannot be empty!!!");
}
}
public boolean invokeAuthentication(String username, String password, Boolean isClientValidation) {
try {
Map<String, Object> userDetails = ldapAuthentication.authenticateUser(username, password);
if(Boolean.parseBoolean(userDetails.get("success").toString())) {
return true;
}
} catch (Exception exception) {
log.error("Exception in invokeAuthentication::: " + exception.getMessage());
}
return false;
}
#Override
public boolean supports(Class<? extends Object> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
private Collection<SimpleGrantedAuthority> fillUserAuthorities(Set<String> roles) {
Collection<SimpleGrantedAuthority> authorties = new ArrayList<SimpleGrantedAuthority>();
for(String role : roles) {
authorties.add(new SimpleGrantedAuthority(role));
}
return authorties;
}
}
Here is you code after modification
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String userName = ((String) authentication.getPrincipal()).toLowerCase();
String password = (String) authentication.getCredentials();
if (userName != null && authentication.getCredentials() != null) {
String clientId = getClientId();
// validate client ID before use
Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
Authentication token = new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
return token;
} else {
throw new BadCredentialsException("Authentication Failed!!!");
}
} else {
throw new BadCredentialsException("Username or Password cannot be empty!!!");
}
private String getClientId(){
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
final String authorizationHeaderValue = request.getHeader("Authorization");
final String base64AuthorizationHeader = Optional.ofNullable(authorizationHeaderValue)
.map(headerValue->headerValue.substring("Basic ".length())).orElse("");
if(StringUtils.isNotEmpty(base64AuthorizationHeader)){
String decodedAuthorizationHeader = new String(Base64.getDecoder().decode(base64AuthorizationHeader), Charset.forName("UTF-8"));
return decodedAuthorizationHeader.split(":")[0];
}
return "";
}
more info about RequestContextHolder
Extend UsernamePasswordAuthenticationToken
A POJO is required to hold not just the username and the password but also the client identifier.
public ExtendedUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
private final String clientId;
public ExtendedUsernamePasswordAuthenticationToken(Object principal
, Object credentials
, String clientId) {
super(principal, credentials);
this.clientId = clientId;
}
public String getClientId() { return clientId; }
}
Extend UsernamePasswordAuthenticationFilter
The authentication process needs to be tweaked so that the client identifier is passed to the authentication code in addition to the username and password.
public class ExtendedUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public ExtendedUsernamePasswordAuthenticationFilter () { super(); }
#Override
public public Authentication attemptAuthentication(HttpServletRequest request
, HttpServletResponse response)
throws AuthenticationException {
// See the source code of UsernamePasswordAuthenticationFilter
// to implement this. Instead of creating an instance of
// UsernamePasswordAuthenticationToken, create an instance of
// ExtendedUsernamePasswordAuthenticationToken, something along
// the lines of:
final String username = obtainUsername(request);
final String password = obtainPassword(request);
final String clientId = obtainClientId(request);
...
final Authentication authentication = new ExtendedUsernamePasswordAuthenticationToken(username, password, clientId);
return getAuthenticationManager().authenticate(authentication);
}
}
Use the extra information available for logging in
public CustomAuthenticationProvider implements AuthenticationProvider {
...
#Override
public boolean supports(final Class<?> authentication) {
return authentication.isAssignableFrom(ExtendedUsernamePasswordAuthenticationToken.class);
}
#Override
public Authentication authenticate(final Authentication authentication)
throws AuthenticationException {
}
}
Force Spring Security to use the custom filter
<bean class="com.path.to.filter.ExtendedUsernamePasswordAuthenticationFilter" id="formAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<http ... >
<security:custom-filter position="FORM_LOGIN_FILTER" ref="formAuthenticationFilter"/>
...
</http>
or, if using Java configuration:
#Bean
public ExtendedUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter(final AuthenticationManager authenticationManager) {
final ExtendedUsernamePasswordAuthenticationFilter filter = new ExtendedUsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
...
}
For your requirement, since you want to just access additional parameters from the request, you could try out the following in your CustomAuthenticationProvider class
#Autowired
private HttpServletRequest request;
Add the following logic to read the httpRequest parameters and add your logic to access the authorization key
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println("Header Name - " + headerName + ", Value - " + request.getHeader(headerName));
}
}
Now, you will have the encode Basic Authentication field which you can decode like the one below
if (authorization != null && authorization.startsWith("Basic")) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic".length()).trim();
String credentials = new String(Base64.getDecoder().decode(base64Credentials),
Charset.forName("UTF-8"));
// client/secret = clientId:secret
final String[] values = credentials.split(":",2);
Related
I implement a custom security feature and I implement a login entry point :
#PostMapping("/login")
public ResponseEntity<UserRestResponseModel> userLogin(#RequestBody UserDetailsRequestModel userDetails) {
if(userDetails.getEmail().isEmpty() || userDetails.getPassword().isEmpty()) {
throw new UserServiceException(ErrorMessages.MISSING_REQUIRED_FIELD.getErrorMessage());
}
authenticate(userDetails.getEmail(), userDetails.getPassword());
UserRestResponseModel userRestResponseModel = new UserRestResponseModel();
ModelMapper modelMapper = new CustomMapper();
//modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
UserDto loggedInUser = userService.getUser(userDetails.getEmail());
if (loggedInUser == null)
throw new UserServiceException("Error !!");
if(loggedInUser.getIsAccountNonLocked() == Boolean.FALSE)
throw new UserServiceException("User account is locked");
userRestResponseModel = modelMapper.map(loggedInUser, UserRestResponseModel.class);
UserPrincipal userPrincipal = modelMapper.map(loggedInUser, UserPrincipal.class);
HttpHeaders jwtHeader = getJwtHeader(userPrincipal);
ResponseEntity<UserRestResponseModel> returnValue =
new ResponseEntity<>(userRestResponseModel, jwtHeader, HttpStatus.OK);
return returnValue;
}
private void authenticate(String userName, String password) {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, password));
}
private HttpHeaders getJwtHeader(UserPrincipal userPrincipal) {
HttpHeaders headers = new HttpHeaders();
String token = jwtTokenProvider.generateJwtToken(userPrincipal);
headers.add(SecurityConstants.TOKEN_PREFIX, token);
return headers;
}
I also implement my UserPrincipal class :
public class UserPrincipal implements UserDetails {
private static final long serialVersionUID = 7464059818443209139L;
private UserEntity userEntity;
private String userKeyId;
public UserPrincipal(){ }
public UserPrincipal(UserEntity userEntity) {
this.userEntity = userEntity;
this.userKeyId = userEntity.getUserKeyId();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new HashSet<>();
Collection<AuthorityEntity> authorityEntities = new HashSet<>();
// get user roles
Collection<RoleEntity> roles = userEntity.getRoles();
if (roles == null) {
return authorities; // null
}
// get user roles
roles.forEach((role) ->{
authorities.add(new SimpleGrantedAuthority(role.getName()));
authorityEntities.addAll(role.getAuthorities());
});
// get user authorities
authorityEntities.forEach(authorityEntity ->
authorities.add(
new SimpleGrantedAuthority(authorityEntity.getName())
)
);
return authorities;
}
#Override
public String getPassword() {
return this.userEntity.getEncryptedPassword();
}
#Override
public String getUsername() {
return this.userEntity.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return this.userEntity.getIsAccountNonExpired();
}
I also have a JwtTokenProvider class that handle token.
#Component
public class JwtTokenProvider {
public String generateJwtToken(UserPrincipal userPrincipal) {
String[] claims = getClaimsFromUser(userPrincipal);
return JWT.create()
.withIssuer(SecurityConstants.TOKEN_ISSUER)
.withAudience(SecurityConstants.TOKEN_AUDIENCE)
.withIssuedAt(new Date())
.withSubject(userPrincipal.getUsername())
.withArrayClaim(SecurityConstants.AUTHORITIES, claims)
.withExpiresAt(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
.sign(Algorithm.HMAC512(SecurityConstants.getTokenSecret().getBytes()));
}
public List<GrantedAuthority> getAuthorities(String token) {
String[] claims = getClaimsFromToken(token);
return stream(claims).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
public Authentication getAuthentication(String userName,
List<GrantedAuthority> authorities,
HttpServletRequest request) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userName, null, authorities);
usernamePasswordAuthenticationToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request));
return usernamePasswordAuthenticationToken;
}
public boolean isTokenValid(String userName, String token) {
JWTVerifier verifier = getJWTVerifier();
return StringUtils.isNotEmpty(userName) && !isTokenExpired(verifier, token);
}
public String getSubject(String token) {
JWTVerifier jwtVerifier = getJWTVerifier();
return jwtVerifier.verify(token).getSubject();
}
private boolean isTokenExpired(JWTVerifier verifier, String token) {
Date expirationDate = verifier.verify(token).getExpiresAt();
return expirationDate.before(new Date());
}
private String[] getClaimsFromToken(String token) {
JWTVerifier verifier = getJWTVerifier();
return verifier.verify(token).getClaim("Authorities").asArray(String.class);
}
private JWTVerifier getJWTVerifier() {
JWTVerifier verifier;
try {
Algorithm algorithm = Algorithm.HMAC512(SecurityConstants.getTokenSecret());
verifier = JWT.require(algorithm).withIssuer(SecurityConstants.TOKEN_ISSUER).build();
} catch (JWTVerificationException e) {
throw new JWTVerificationException(SecurityConstants.TOKEN_CANNOT_BE_VERIFIED);
}
return verifier;
}
private String[] getClaimsFromUser(UserPrincipal userPrincipal) {
List<String> authorities = new ArrayList<>();
for(GrantedAuthority grantedAuthority: userPrincipal.getAuthorities()) {
authorities.add(grantedAuthority.getAuthority());
}
return authorities.toArray(new String[0]);
}
}
Everything works fine except that I have 2 issues :
If i don't have a no argument constructor in my UserPrincipal class, model mapper returns an error...
"message": "An error occurred while processing the request ModelMapper mapping errors: Failed to instantiate instance of destination UserPrincipal. Ensure that UserPrincipal has a non-private no-argument constructor. 1 error".
And if i do add a no argument constructor, I have a null pointer exception (userPrincipal is null) with a nice 500 error. it drives me crazy.
My issue is around this place in my code:
UserDto loggedInUser = userService.getUser(userDetails.getEmail());
...
userRestResponseModel = modelMapper.map(loggedInUser, UserRestResponseModel.class);
UserPrincipal userPrincipal = modelMapper.map(loggedInUser, UserPrincipal.class);
HttpHeaders jwtHeader = getJwtHeader(userPrincipal);
//controller
#PostMapping("/authenticate")
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
#PostMapping("/register") public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception { return ResponseEntity.ok(userDetailsService.save(user)); }
//service
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
new ArrayList<>());
}
public User save(UserDTO user) {
User newUser = new User(); newUser.setUsername(user.getUsername()); newUser.setEmail(user.getEmail()); newUser.setPassword(bcryptEncoder.encode(user.getPassword())); return service.storeUser(newUser);
}
//Websecurity configuration file
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Autowired
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// We don't need CSRF for this example
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/authenticate", "/register").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
//jwtrequestfilter
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
I am new to Springboot and im trying to filter requests through a Zuul API gateway however i get the error below :
AnonymousAuthenticationToken cannot be cast to org.aacctt.ms.auth.security.JWTAuthentication
When i put a breakpoint i get a null header/token string value when the request reaches the authentication service from zuul gateway, this happens for protected requests that require an authorization token.
My aim is to be able to verify the token sent by clients so that i can allow the client's request to protected endpoints or reject it.
Im not sure what im doing wrong here is my code:
Auth Service
#Component
public class JWTAuthorizationFilter extends GenericFilterBean {
private static final Logger LOG = LoggerFactory.getLogger(JWTAuthorizationFilter.class);
private static final String HEADER_STRING = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
#Value("${jwt.encryption.secret}")
private String SECRET;
#Value("${jwt.access.token.expiration.seconds}")
private long EXPIRATION_TIME_IN_SECONDS;
public String generateAccessToken(long userId) {
return JWT.create()
.withSubject(String.valueOf(userId))
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME_IN_SECONDS * 1000))
.sign(Algorithm.HMAC256(SECRET.getBytes()));
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String header = httpRequest.getHeader(HEADER_STRING); // this is null
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(httpRequest, httpResponse);
return;
}
SecurityContextHolder.getContext().setAuthentication(getAuthentication(header));
chain.doFilter(httpRequest, httpResponse);
}
private Authentication getAuthentication(String token) {
final String username;
try {
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""));
username = jwt.getSubject();
} catch (JWTVerificationException e) {
LOG.debug("Invalid JWT", e);
return null;
}
final Long userId;
try {
userId = Long.valueOf(username);
} catch (NumberFormatException e) {
LOG.debug("Invalid JWT. Username is not an user ID");
return null;
}
LOG.debug("Valid JWT. User ID: " + userId);
return new JWTAuthentication(userId);
}
}
WebSecurityConfig
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JWTAuthorizationFilter jwtAuthorizationFilter;
public WebSecurityConfig(JWTAuthorizationFilter jwtAuthorizationFilter) {
this.jwtAuthorizationFilter = jwtAuthorizationFilter;
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().disable();
http.csrf().disable();
http.addFilterAfter(jwtAuthorizationFilter, BasicAuthenticationFilter.class);
http.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers(AccountController.PATH_POST_SIGN_UP).permitAll()
.antMatchers(AccountController.PATH_POST_REFRESH).permitAll()
.antMatchers(AccountController.PATH_POST_LOGIN).permitAll()
.antMatchers("/v2/api-docs",
"/swagger-resources/configuration/ui",
"/swagger-resources",
"/swagger-resources/configuration/security",
"/swagger-ui.html",
"/webjars/**").permitAll()
.anyRequest().authenticated()
;
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
JWTAuthentication
public class JWTAuthentication implements Authentication {
private final long userId;
public JWTAuthentication(long userId) {
this.userId = userId;
}
#Override public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptySet();
}
#Override public Object getCredentials() {
return null;
}
#Override public Object getDetails() {
return null;
}
public long getUserId() {
return userId;
}
#Override public Long getPrincipal() {
return userId;
}
#Override public boolean isAuthenticated() {
return true;
}
#Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
throw new UnsupportedOperationException("JWT authentication is always authenticated");
}
#Override public String getName() {
return String.valueOf(userId);
}
}
SecurityService
#Service
public class SecurityService {
public long getLoggedUserId() {
JWTAuthentication authentication = (JWTAuthentication) SecurityContextHolder.getContext().getAuthentication();
return authentication.getUserId();
}
}
Zuul Gateway
public class AuthorizationFilter extends BasicAuthenticationFilter {
private static final Logger LOG = LoggerFactory.getLogger(AuthorizationFilter.class);
private static final String HEADER_STRING = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
Environment environment;
public AuthorizationFilter(AuthenticationManager authManager, Environment environment) {
super(authManager);
this.environment = environment;
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String authorizationHeader = req.getHeader(environment.getProperty("authorization.token.header.name"));
if (authorizationHeader == null || !authorizationHeader.startsWith(environment.getProperty("authorization.token.header.prefix"))) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req) {
String token = req.getHeader(HEADER_STRING);
final String username;
try {
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(environment.getProperty("token.secret").getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""));
username = jwt.getSubject();
} catch (JWTVerificationException e) {
LOG.debug("Invalid JWT", e);
return null;
}
final Long userId;
try {
userId = Long.valueOf(username);
} catch (NumberFormatException e) {
LOG.debug("Invalid JWT. Username is not an user ID");
return null;
}
LOG.debug("Valid JWT. User ID: " + userId);
return new UsernamePasswordAuthenticationToken(userId, null, new ArrayList<>());
}
}
The issue is the sensitive header, Authorization is sensitive header by default in Zuul, you just need to override the sensitive headers.
zuul:
sensitive-headers:
-
By setting this property in Zuul gateway application.yml it route request to auth service with the Authorization header
Reference only:
Auth Service reference
JWT based authentication
https://github.com/nikhilmalavia/SpringBootJWT.git
I have tried to send JWT Token in Zuul Header to another microservice module. Each Time request goes from zuul to another module but. I always get null header in another module. But I obtain token in zuul server from auth server but it never reaches to another module.
public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {
private final JwtConfig jwtConfig;
public JwtTokenAuthenticationFilter(JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
}
private static final int FILTER_ORDER = 0;
private static final boolean SHOULD_FILTER = true;
private static final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request1, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String header = request1.getHeader(jwtConfig.getHeader());
if (header == null || !header.startsWith(jwtConfig.getPrefix())) {
chain.doFilter(request1, response);
return;
}
/* new token getting code*/
String token = header.replace(jwtConfig.getPrefix(), "");
try {
Claims claims = Jwts.parser()
.setSigningKey(jwtConfig.getSecret().getBytes())
.parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
System.out.println(username);
if (username != null) {
#SuppressWarnings("unchecked")
List<String> authorities = (List<String>) claims.get("authorities");
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(
username,
null, authorities.stream().map(
SimpleGrantedAuthority::new
).collect(Collectors.toList()));
SecurityContextHolder.getContext().setAuthentication(auth);
}
} catch (Exception e) {
SecurityContextHolder.clearContext();
}
System.out.println(String.format("%s request to %s", request1.getMethod(), request1.getRequestURL().toString()));
/* return null;*/
request1.setAttribute("header",token);
chain.doFilter(request1, response);
}
}
In your application.properties of zuul you should add zuul.sensitiveHeaders=Cookie,Set-Cookie
I have a customAuthenticationProvider and I want to authenticate user with 3 parameters: username, password and tokenPin.
But at the moment I have a little problem with this provider:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private UserService userService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
String pin = ????
Authentication auth = null;
User user = userService.findByUsernameAndPassword(username, password);
if (user != null) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
auth = new UsernamePasswordAuthenticationToken(username, password, grantedAuths);
}
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}