I'm using the Spring boot resource server. The authentication server issues a JWT. This JWT is re-encoded(with AES) with a key and in the Resource server, I should decode the JWT (from AES) before sending it to the JwtAuthenticator.
Now, I have a security configuration.
#Override
protected void configure(HttpSecurity http) throws Exception {
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new KeycloakRoleConverter());
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/users/status/check")
.hasRole("developer")
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.decoder(new JWTDecoder())
.jwtAuthenticationConverter(jwtAuthenticationConverter);
}
and a JWT Decoder
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtException;
import java.text.ParseException;
public class JWTDecoder implements JwtDecoder {
#Override
public Jwt decode(String token) throws JwtException {
//decrypt from AES here
JWT jwt = null;
try {
jwt = JWTParser.parse(token);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
What should I do then? The function should return org.springframework.security.oauth2.jwt.Jwt. How can I convert String token to Jwt?
I tried the following, but a problem occurred.
private Jwt createJwt(String token, JWT parsedJwt) {
try {
Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
Map<String, Object> claims = parsedJwt.getJWTClaimsSet().getClaims();
return Jwt.withTokenValue(token)
.headers(h -> h.putAll(headers))
.claims(c -> c.putAll(claims))
.build();
} catch (Exception ex) {
if (ex.getCause() instanceof ParseException) {
throw new JwtException("There is a problem parsing the JWT.");
} else {
throw new JwtException("There is a problem decoding the JWT.");
}
}
}
The error I received:
java.lang.IllegalArgumentException: timestamps must be of type Instant: java.lang.Long
I'm using Keycloak to generate the JWT. So, the exp field of the token in the jwt.io is "exp": 1657363340,. But after parsing the JWT in my code, It changes to the Date format. So, I changed the exp to Instant and my final method is like the following:
private Jwt createJwt(String token, JWT parsedJwt) {
try {
Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
Map<String, Object> claims = parsedJwt.getJWTClaimsSet().getClaims();
Jwt.Builder finalJwt = Jwt.withTokenValue(token)
.headers(h -> h.putAll(headers))
.claims(c -> c.putAll(claims));
finalJwt.expiresAt(((Date) claims.get("exp")).toInstant());
return finalJwt.build();
} catch (Exception ex) {
if (ex.getCause() instanceof ParseException) {
throw new JwtException("There is a problem parsing the JWT: " + ex.getMessage());
} else {
throw new JwtException("There is a problem decoding the JWT: " + ex.getMessage());
}
}
}
But the problem exists yet.
It could be due to expiration date of your token is a timestamp, and it should be a number (Long). Or you are trying to parse a timestamp to number Long.
As #Jose told me, I set the value of expiration time with an Instant type of the timestamp. Then, I set it to both the exp and iat fields of the JWT. My final function is like the following:
Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
Map<String, Object> claims = new HashMap<>();
for (String key : parsedJwt.getJWTClaimsSet().getClaims().keySet()) {
Object value = parsedJwt.getJWTClaimsSet().getClaims().get(key);
if (key.equals("exp") || key.equals("iat")) {
value = ((Date) value).toInstant();
}
claims.put(key, value);
}
return Jwt.withTokenValue(token)
.headers(h -> h.putAll(headers))
.claims(c -> c.putAll(claims))
.build();
Related
I have a code with a RestBuilder that needs to connect to another application, the target endPoint have an object in the signature with the attributes. The problem is the request return 404. How I solve this? I tried use x-www-form-urlencoded (doesn't work)
Request Method:
RestResponse restResponse;
String parameters = '{"qtdThreads":3,"channel":"http://localhost:8081/application2"}'
try {
restResponse = new RestBuilder().post("http://localhost:8080/application/endPoint", {
accept("application/json")
contentType "application/json; utf-8"
body(parameters.getBytes("UTF-8"))
connectTimeout: 1000
})
} catch (Exception e) {
e.printStackTrace();
} finally {
return restResponse;
}
Target endPoint:
Object endPoint(ObjectCommand command) {
render(status: HttpStatus.OK)
}
Object used on signature
import grails.validation.Validateable
#Validateable
class ObjectCommand {
URL channel
Integer qtdThreads
static constraints = {
qtdThreads(validator: { Integer val ->
if (!val || val <= 0) {
return "message1"
}
})
channel(validator: { URL val ->
if (!val) {
return "message2"
}
})
}
}
did you check if the target application is running and exposing that endpoint?
I'm developing a Java application (using Spring Boot) and I need some help:
This application receives as input a JWT token which I process in a method. The method for now is the following:
private UsernamePasswordAuthenticationToken myMethod(HttpServletRequest request) {
// Code that gets the 'token' String
try{
Map<String, String> registry = ((Map<String, String>) (token.getBody().get("registry")));
String sub = ((String) (parsedToken.getBody().get("sub")));
UsernamePasswordAuthenticationToken finalToken = new UsernamePasswordAuthenticationToken(sub, null, null);
return finalToken;
} catch (ExpiredJwtException exception) { // Only here I have the certainty that the token has expired!
// Code that handles the exception
}
}
However, I need to implement a logic that must check in several places whether the token obtained has expired or not, without running this method every time. The only way I have to know if token has expired is the exception raised by ExpiredJwtException.
Is there any way to know if the token has expired without going through the catched exception? For example, it would be very useful if there was a "token" class that has an .isExpired attribute, or something like that.
I don't want to go into handling the exception because it means that I would always depend on the (long) code of the try block every time I need to check if a token has expired or not, and I don't want it to be.
If you use a different JWT library, you can do it easily enough. The auth0 JWT library has methods to parse and, optionally, verify the token:
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
DecodedJWT jwt = JWT.decode(token);
if( jwt.getExpiresAt().before(new Date())) {
System.out.println("token is expired");
}
For this you can create another class for handling the token validation like below, in the class you will handle the ExpiredJwtException and give the extracted value back , other exceptions will throw it from the class
public class TokenValidator {
Boolean tokenExpired = true;
public boolean isTokenValid(String token) throws Exception {
validateToken(token);
return tokenExpired;
}
public Map<String,String> getExtractedData(String token) throws Exception {
return validateToken(token);
}
private Map<String, String> validateToken (String token) throws Exception{
try {
Claims claims = Jwts.parser().setSigningKey("jwtSecretKey")
.parseClaimsJws(token).getBody();
tokenExpired = false;
return getClaimsInMap(claims);
} catch (ExpiredJwtException ex) {
DefaultClaims claims = (DefaultClaims) ex.getClaims();
return getClaimsInMap(claims);
} catch (Exception e) {
throw new Exception(e);
}
}
private Map<String,String> getClaimsInMap(Claims claims) {
Map<String,String> expectedMap = new HashMap<>();
claims.entrySet().stream().forEach(entry -> expectedMap.put(entry.getKey(),entry.getValue().toString()));
return expectedMap;
}
}
This can be achieved by using claims. Below sample code can help.
// Get Expiration and compare it with new Date()
public boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token , Function<Claims, T> claimResolver) {
final Claims claim= extractAllClaims(token);
return claimResolver.apply(claim);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
We are using spring boot as backend to for only auth and generating jwt token rest is handled in hasura.
I am facing problem in generating JWT properly.
public String generateToken(String email,String role,Long id) {
Map<String, Object> claims = new HashMap<>();
Map<String,Object> claim =new HashMap<>();
claims.put("x-hasura-user-id",id);
claims.put("x-hasura-default-role",role);
claims.put("x-hasura-allowed-roles", new String[]{"job_seeker", "employer", "admin"});
claim.put("https://hasura.io/jwt/claims",claims);
System.out.println(claim);
return doGenerateToken(claim, email);
}
private String doGenerateToken(Map<String, Object> claim, String subject) {
return Jwts.builder().setClaims(claim).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + Long.parseLong(tokenValidity))).signWith(SignatureAlgorithm.HS256, secret).compact();
}
This is generating jwt token as
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJyb3NhbjEyM0BnbWFpbC5jb20iLCJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6ImFkbWluIiwieC1oYXN1cmEtdXNlci1pZCI6NCwieC1oYXN1cmEtYWxsb3dlZC1yb2xlcyI6WyJqb2Jfc2Vla2VyIiwiZW1wbG95ZXIiLCJhZG1pbiJdfSwiZXhwIjoxNjA5ODU1OTA2LCJpYXQiOjE2MDk4NTExMDZ9.WqJE1xLIsycW92tzFXdq0UHub3qUfQbUvUax9rvks4Q
but it hasura is returning Invalid signature. Where as in node
generateToken: (user: any) => {
const payload = {
sub: user.email,
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": `${user.role.name}`,
"x-hasura-user-id": `${user.id}`,
"x-hasura-allowed-roles": ["job_seeker", "employer", "admin"],
},
};
return jwt.sign(payload, secretkey);
},
jwt from node
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib2hhcmFuaXNjaGFsQGdtYWlsLmNvbSIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiam9iX3NlZWtlciIsIngtaGFzdXJhLXVzZXItaWQiOiIyNCIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsiam9iX3NlZWtlciIsImVtcGxveWVyIiwiYWRtaW4iXX0sImlhdCI6MTYwOTg1NDEzMX0.8UDrqvRujakGsEtGEAu1XWl5RsFda8HaA_-97vwY62I
using same secret key and algorithm is working perfectly fine. For node i have used jsonwebtoken library.
I found the solution. String should be converted to byte[]:
private String doGenerateToken(Map<String,Object> header,Map<String, Object> claim, String subject) {
return Jwts.builder().setHeader(header).setClaims(claim).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + Long.parseLong(tokenValidity))).signWith(SignatureAlgorithm.HS256, secret.getBytes(StandardCharsets.UTF_8)).compact();
}
I need to send FilePart received in RestController to API using WebClient,
how can I do this?
Found an example, which saves image to disk.
private static String UPLOAD_ROOT = "C:\\pics\\";
public Mono<Void> checkInTest(#RequestPart("photo") Flux<FilePart> photoParts,
#RequestPart("data") CheckInParams params, Principal principal) {
return saveFileToDisk(photoParts);
}
private Mono<Void> saveFileToDisk(Flux<FilePart> parts) {
return parts
.log("createImage-files")
.flatMap(file -> {
Mono<Void> copyFile = Mono.just(Paths.get(UPLOAD_ROOT, file.filename()).toFile())
.log("createImage-picktarget")
.map(destFile -> {
try {
destFile.createNewFile();
return destFile;
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.log("createImage-newfile")
.flatMap(file::transferTo)
.log("createImage-copy");
return Mono.when(copyFile)
.log("createImage-when");
})
.log("createImage-flatMap")
.then()
.log("createImage-done");
}
Then read it again and send to anoter server
.map(destFile -> {
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
try {
map.set("multipartFile", new ByteArrayResource(FileUtils.readFileToByteArray(destFile)));
} catch (IOException ignored) {
}
map.set("fileName", "test.txt");
WebClient client = WebClient.builder().baseUrl("http://localhost:8080").build();
return client.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.syncBody(map)
.exchange(); //todo handle errors???
}).then()
Is there way to avoid saving file?
I will mention solution by #Abhinaba Chakraborty
provided in https://stackoverflow.com/a/62745370/4551411
Probably something like this:
#PostMapping(value = "/images/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<ResponseEntity<Void>> uploadImages(#RequestPart("files") Flux<FilePart> fileParts) {
return fileParts
.flatMap(filePart -> {
return webClient.post()
.uri("/someOtherService")
.body(BodyInserters.fromPublisher(filePart.content(), DataBuffer.class))
.exchange()
.flatMap(clientResponse -> {
//some logging
return Mono.empty();
});
})
.collectList()
.flatMap(response -> Mono.just(ResponseEntity.accepted().build()));
}
This accepts MULTIPART FORM DATA where you can attach multiple image files and upload them to another service.
Using Spring Boot I am configuring the following filter
#Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
Map<String, String> filterChainDefinitionMapping = new HashMap<>();
/*
* URL path expressions are evaluated against an incoming request in the order they are defined and the FIRST MATCH WINS. For example, let's asume that there are the following chain definitions:
/account/** = ssl, authc
/account/signup = anon
If an incoming request is intended to reach /account/signup/index.html (accessible by all 'anon'ymous users), it will never be handled!. The reason is that the /account/** pattern matched the incoming request first and 'short-circuited' all remaining definitions.
Always remember to define your filter chains based on a FIRST MATCH WINS policy!
* */
filterChainDefinitionMapping.put("/login.html", "authc");
filterChainDefinitionMapping.put("/logout", "logout");
filterChainDefinitionMapping.put("/css/**", "anon");
filterChainDefinitionMapping.put("/register/**", "anon");
filterChainDefinitionMapping.put("/**", "authc");
shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMapping);
shiroFilter.setSecurityManager(securityManager());
shiroFilter.setLoginUrl("/login.html");
shiroFilter.setSuccessUrl("/");
shiroFilter.setUnauthorizedUrl("/unauthorized.html");
Map<String, Filter> filters = new HashMap<>();
filters.put("anon", new AnonymousFilter());
filters.put("authc", new FormAuthenticationFilter());
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/login.html?logout");
filters.put("logout", logoutFilter);
filters.put("roles", new RolesAuthorizationFilter());
filters.put("user", new UserFilter());
shiroFilter.setFilters(filters);
return shiroFilter;
}
However, whenever I try to login with wrong credentials the redirection never happens. I do get the "shiroLoginFailure" attribute holding the UnknownUserException.
(Logging in with the correct credentials works fine)
Any ideas?
Mariosk89, how do you resolve the /login.html?
It might be need to resolve redirect like this:
#RequestMapping("/login")
public String login(String username, String password) {
Subject currentUser = SecurityUtils.getSubject();
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
try {
currentUser.login(new UsernamePasswordToken(username, password));
} catch (Exception e) {
logger.error(e.getLocalizedMessage(), e);
return "login";
}
return "redirect:index";
} else {
return "login";
}
}
Reference: https://github.com/lenicliu/examples/tree/master/examples-spring-boot/examples-spring-boot-shiro
For more exception solution, refer http://shiro.apache.org/10-minute-tutorial.html
try {
currentUser.login( token );
//if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
//username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {
//password didn't match, try again?
} catch ( LockedAccountException lae ) {
//account for that username is locked - can't login. Show them a message?
}
... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
//unexpected condition - error?
}