JWT (JSON Web Token) library for Java [closed] - java

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 3 years ago.
Improve this question
I am working on a web application developed using Java and AngularJS and chose to implement token authentication and authorization.
For the exercise purpose, I've come to the point where I send the credentials to the server, generate a random token store it and send it back to the client.
At every request to the server I'm attaching the token in the header and it works perfectly.
For the authentication point of view is perfect and wouldn't need more.
However, I now want to keep track of the user type (admin, regular user...), as well as it's id, or any other unique field; as I understood I have to encrypt that in the token that I'm sending back to the client during the log in action. Is that correct?
Is there any JWT library that you used and can generate, encrypt and decrypt such tokens?
A link to the library's API and Maven dependency would be much appreciated.
Thanks

JJWT aims to be the easiest to use and understand JWT library for the JVM and Android:
https://github.com/jwtk/jjwt

If anyone in the need for an answer,
I used this library: http://connect2id.com/products/nimbus-jose-jwt
Maven here: http://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt/2.10.1

By referring to https://jwt.io/ you can find jwt implementations in many languages including java. Also the site provide some comparison between these implementation (the algorithms they support and ....).
For java these are mentioned libraries:
https://github.com/jwtk/jjwt
https://github.com/auth0/java-jwt ( A tutorial at https://auth0.com/docs/server-apis/java)
https://bitbucket.org/b_c/jose4j
https://bitbucket.org/connect2id/nimbus-jose-jwt

This library seems to work well: https://code.google.com/p/jsontoken/ .
It depends on Google Guava. Here are the Maven artifacts:
<dependency>
<groupId>com.googlecode.jsontoken</groupId>
<artifactId>jsontoken</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
The library is in fact used by Google Wallet.
Here is how to create a jwt, and to verify it and deserialize it:
import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.util.Calendar;
import java.util.List;
import net.oauth.jsontoken.JsonToken;
import net.oauth.jsontoken.JsonTokenParser;
import net.oauth.jsontoken.crypto.HmacSHA256Signer;
import net.oauth.jsontoken.crypto.HmacSHA256Verifier;
import net.oauth.jsontoken.crypto.SignatureAlgorithm;
import net.oauth.jsontoken.crypto.Verifier;
import net.oauth.jsontoken.discovery.VerifierProvider;
import net.oauth.jsontoken.discovery.VerifierProviders;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
import com.google.common.collect.Lists;
import com.google.gson.JsonObject;
/**
* Provides static methods for creating and verifying access tokens and such.
* #author davidm
*
*/
public class AuthHelper {
private static final String AUDIENCE = "NotReallyImportant";
private static final String ISSUER = "YourCompanyOrAppNameHere";
private static final String SIGNING_KEY = "LongAndHardToGuessValueWithSpecialCharacters#^($%*$%";
/**
* Creates a json web token which is a digitally signed token that contains a payload (e.g. userId to identify
* the user). The signing key is secret. That ensures that the token is authentic and has not been modified.
* Using a jwt eliminates the need to store authentication session information in a database.
* #param userId
* #param durationDays
* #return
*/
public static String createJsonWebToken(String userId, Long durationDays) {
//Current time and signing algorithm
Calendar cal = Calendar.getInstance();
HmacSHA256Signer signer;
try {
signer = new HmacSHA256Signer(ISSUER, null, SIGNING_KEY.getBytes());
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
//Configure JSON token
JsonToken token = new net.oauth.jsontoken.JsonToken(signer);
token.setAudience(AUDIENCE);
token.setIssuedAt(new org.joda.time.Instant(cal.getTimeInMillis()));
token.setExpiration(new org.joda.time.Instant(cal.getTimeInMillis() + 1000L * 60L * 60L * 24L * durationDays));
//Configure request object, which provides information of the item
JsonObject request = new JsonObject();
request.addProperty("userId", userId);
JsonObject payload = token.getPayloadAsJsonObject();
payload.add("info", request);
try {
return token.serializeAndSign();
} catch (SignatureException e) {
throw new RuntimeException(e);
}
}
/**
* Verifies a json web token's validity and extracts the user id and other information from it.
* #param token
* #return
* #throws SignatureException
* #throws InvalidKeyException
*/
public static TokenInfo verifyToken(String token)
{
try {
final Verifier hmacVerifier = new HmacSHA256Verifier(SIGNING_KEY.getBytes());
VerifierProvider hmacLocator = new VerifierProvider() {
#Override
public List<Verifier> findVerifier(String id, String key){
return Lists.newArrayList(hmacVerifier);
}
};
VerifierProviders locators = new VerifierProviders();
locators.setVerifierProvider(SignatureAlgorithm.HS256, hmacLocator);
net.oauth.jsontoken.Checker checker = new net.oauth.jsontoken.Checker(){
#Override
public void check(JsonObject payload) throws SignatureException {
// don't throw - allow anything
}
};
//Ignore Audience does not mean that the Signature is ignored
JsonTokenParser parser = new JsonTokenParser(locators,
checker);
JsonToken jt;
try {
jt = parser.verifyAndDeserialize(token);
} catch (SignatureException e) {
throw new RuntimeException(e);
}
JsonObject payload = jt.getPayloadAsJsonObject();
TokenInfo t = new TokenInfo();
String issuer = payload.getAsJsonPrimitive("iss").getAsString();
String userIdString = payload.getAsJsonObject("info").getAsJsonPrimitive("userId").getAsString();
if (issuer.equals(ISSUER) && !StringUtils.isBlank(userIdString))
{
t.setUserId(new ObjectId(userIdString));
t.setIssued(new DateTime(payload.getAsJsonPrimitive("iat").getAsLong()));
t.setExpires(new DateTime(payload.getAsJsonPrimitive("exp").getAsLong()));
return t;
}
else
{
return null;
}
} catch (InvalidKeyException e1) {
throw new RuntimeException(e1);
}
}
}
public class TokenInfo {
private ObjectId userId;
private DateTime issued;
private DateTime expires;
public ObjectId getUserId() {
return userId;
}
public void setUserId(ObjectId userId) {
this.userId = userId;
}
public DateTime getIssued() {
return issued;
}
public void setIssued(DateTime issued) {
this.issued = issued;
}
public DateTime getExpires() {
return expires;
}
public void setExpires(DateTime expires) {
this.expires = expires;
}
}
This is based on code here: https://developers.google.com/wallet/instant-buy/about-jwts
And Here: https://code.google.com/p/wallet-online-sample-java/source/browse/src/com/google/wallet/online/jwt/util/WalletOnlineService.java?r=08b3333bd7260b20846d7d96d3cf15be8a128dfa

IETF has suggested jose libs on it's wiki:
http://trac.tools.ietf.org/wg/jose/trac/wiki
I would highly recommend using them for signing. I am not a Java guy, but seems like jose4j seems like a good option. Has nice examples as well: https://bitbucket.org/b_c/jose4j/wiki/JWS%20Examples
Update: jwt.io provides a neat comparison of several jwt related
libraries, and their features. A must check!
I would love to hear about what other java devs prefer.

I found this to be small and complete https://github.com/auth0/java-jwt

This page keeps references to implementations in various languages, including Java, and compares features: http://kjur.github.io/jsjws/index_mat.html

If you only need to parse unsigned unencrypted tokens you could use this code:
boolean parseJWT_2() {
String authToken = getToken();
String[] segments = authToken.split("\\.");
String base64String = segments[1];
int requiredLength = (int)(4 * Math.ceil(base64String.length() / 4.0));
int nbrPaddings = requiredLength - base64String.length();
if (nbrPaddings > 0) {
base64String = base64String + "====".substring(0, nbrPaddings);
}
base64String = base64String.replace("-", "+");
base64String = base64String.replace("_", "/");
try {
byte[] data = Base64.decode(base64String, Base64.DEFAULT);
String text;
text = new String(data, "UTF-8");
tokenInfo = new Gson().fromJson(text, TokenInfo.class);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}

https://github.com/networknt/jsontoken
This is a fork of original google jsontoken
It has not been updated since Sep 11, 2012 and depends on some old packages.
What I have done:
Convert from Joda time to Java 8 time. So it requires Java 8.
Covert Json parser from Gson to Jackson as I don't want to include two Json parsers to my projects.
Remove google collections from dependency list as it is stopped long time ago.
Fix thread safe issue with Java Mac.doFinal call.
All existing unit tests passed along with some newly added test cases.
Here is a sample to generate token and verify the token. For more information, please check https://github.com/networknt/light source code for usage.
I am the author of both jsontoken and Omni-Channel Application Framework.

Related

DynamoDB Table Partition key and sort key are 1:1 - how to go about querying only using partition key?

I am attempting to query a table that has a partition key and sort key (however the partition key and sort key are 1:1 and I want to query only using the partition key [in which only one item would be returned]).
QueryRequest query = new QueryRequest()
.withTableName(TABLE_NAME)
.withKeyConditionExpression("testId = :" + "1234567890");
QueryResult result = client.query(query);
This is the code I tried but it did not work (testId is the partition key name and 1234567890 is the partition key value in String form); do y'all know of a method I could use to query by only using the partition key keeping in mind that only one item will be returned since the partition key and sort key are 1:1? Thank you so much in advance. [This is my first Stack Overflow post - my apologies if I worded things poorly, I'm happy to answer any questions about my wording]
FYI: this is the error statement I got when trying to use the code above:
errorMessage": "Invalid KeyConditionExpression: An expression attribute value used in expression is not defined
You should really update to use AWS SDK For Java V2 (Using AWS SDK for V1 is not best practice). Using AWS SDK for Java v2 is best practice for using Amazon DynamoDB API.
To learn more about the AWS V2 Java API, read the Developer Guide here.
Developer guide - AWS SDK for Java 2.x
Now I will answer this question with V2. The solution that worked for me was create a secondary index named year-index. This uses just my partition key named year (and does not use the sort key).
I can successfully query using this index, as shown here that uses the AWS Management Console.
Now only movies with the year 2014 are returned. That is how you query when your table has a composite key made up of a partition key and sort key and you only want to query on partition key.
By the way - you said you have a secondary index. A table can have more then 1 secondary index
Code that you need for V2 to query a secondary Index
I will show you how to use V2 to search for secondary index using three ways.
First way - Use the V2 Enhanced Client
Once you create the secondary index, you can use it to query. As mentioned, I created a secondary index named year-index. I can use this secondary index to query data by using the DynamoDB Enhanced Client.
Because, I am querying the Movies table, I have to create a Class named Movies like this. Notice the use of the #DynamoDbSecondaryPartitionKey annotation.
package com.example.dynamodb;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;
#DynamoDbBean
public class Movies {
private String title;
private int year;
private String info;
#DynamoDbSecondaryPartitionKey(indexNames = { "year-index" })
#DynamoDbPartitionKey
public int getYear() {
return this.year;
}
public void setYear(int year) {
this.year = year;
}
#DynamoDbSortKey
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getInfo() {
return this.info;
}
public void setInfo(String info) {
this.info = info;
}
}
Finally, here is the V2 code that lets you query using the secondary index.
package com.example.dynamodb;
// snippet-start:[dynamodb.java2.get_item_index.import]
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.core.pagination.sync.SdkIterable;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbIndex;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.Page;
import software.amazon.awssdk.enhanced.dynamodb.model.QueryConditional;
import software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import java.util.List;
// snippet-end:[dynamodb.java2.get_item_index.import]
/**
* Before running this Java V2 code example, set up your development environment, including your credentials.
*
* For more information, see the following documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*
* To get an item from an Amazon DynamoDB table using the AWS SDK for Java V2, its better practice to use the
* Enhanced Client, see the EnhancedGetItem example.
*
* Create the Movies table by running the Scenario example and loading the Movies data from the JSON file. Next create a secondary
* index for the Movies table that uses only the year column. Name the index **year-index**. For more information, see:
*
* https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html
*/
public class EnhancedGetItemUsingIndex {
public static void main(String[] args) {
String tableName = "Movies" ; //args[0];
ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.create();
Region region = Region.US_EAST_1;
DynamoDbClient ddb = DynamoDbClient.builder()
.credentialsProvider(credentialsProvider)
.region(region)
.build();
queryIndex(ddb, tableName);
ddb.close();
}
// snippet-start:[dynamodb.java2.get_item_index.main]
public static void queryIndex(DynamoDbClient ddb, String tableName) {
try {
// Create a DynamoDbEnhancedClient and use the DynamoDbClient object.
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
.dynamoDbClient(ddb)
.build();
//Create a DynamoDbTable object based on Movies.
DynamoDbTable<Movies> table = enhancedClient.table("Movies", TableSchema.fromBean(Movies.class));
String dateVal = "2013";
DynamoDbIndex<Movies> secIndex =
enhancedClient.table("Movies", TableSchema.fromBean(Movies.class))
.index("year-index");
AttributeValue attVal = AttributeValue.builder()
.n(dateVal)
.build();
// Create a QueryConditional object that's used in the query operation.
QueryConditional queryConditional = QueryConditional
.keyEqualTo(Key.builder().partitionValue(attVal)
.build());
// Get items in the table.
SdkIterable<Page<Movies>> results = secIndex.query(
QueryEnhancedRequest.builder()
.queryConditional(queryConditional)
.limit(300)
.build());
//Display the results.
results.forEach(page -> {
List<Movies> allMovies = page.items();
for (Movies myMovies: allMovies) {
System.out.println("The movie title is " + myMovies.getTitle() + ". The year is " + myMovies.getYear());
}
});
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
// snippet-end:[dynamodb.java2.get_item_index.main]
}
This now returns all Movies where the year is 2013.
Second way - Use the V2 Service Client
package com.example.dynamodb;
// snippet-start:[dynamodb.java2.query_items_sec_index.import]
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.QueryRequest;
import software.amazon.awssdk.services.dynamodb.model.QueryResponse;
import java.util.HashMap;
import java.util.Map;
// snippet-end:[dynamodb.java2.query_items_sec_index.import]
/**
* Before running this Java V2 code example, set up your development environment, including your credentials.
*
* For more information, see the following documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*
* Create the Movies table by running the Scenario example and loading the Movies data from the JSON file. Next create a secondary
* index for the Movies table that uses only the year column. Name the index **year-index**. For more information, see:
*
* https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html
*/
public class QueryItemsUsingIndex {
public static void main(String[] args) {
String tableName = "Movies" ; //args[0];
ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.create();
Region region = Region.US_EAST_1;
DynamoDbClient ddb = DynamoDbClient.builder()
.credentialsProvider(credentialsProvider)
.region(region)
.build();
queryIndex(ddb, tableName);
ddb.close();
}
// snippet-start:[dynamodb.java2.query_items_sec_index.main]
public static void queryIndex(DynamoDbClient ddb, String tableName) {
try {
Map<String,String> expressionAttributesNames = new HashMap<>();
expressionAttributesNames.put("#year","year");
Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":yearValue", AttributeValue.builder().n("2013").build());
QueryRequest request = QueryRequest.builder()
.tableName(tableName)
.indexName("year-index")
.keyConditionExpression("#year = :yearValue")
.expressionAttributeNames(expressionAttributesNames)
.expressionAttributeValues(expressionAttributeValues)
.build();
System.out.println("=== Movie Titles ===");
QueryResponse response = ddb.query(request);
response.items()
.forEach(movie-> System.out.println(movie.get("title").s()));
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
// snippet-end:[dynamodb.java2.query_items_sec_index.main]
}
**Third way - Use PartQL
Of course, you can query the partition key using PartiQL. For example.
public static void queryTable(DynamoDbClient ddb) {
String sqlStatement = "SELECT * FROM MoviesPartiQ where year = ? ORDER BY info";
try {
List<AttributeValue> parameters = new ArrayList<>();
AttributeValue att1 = AttributeValue.builder()
.n(String.valueOf("2013"))
.build();
parameters.add(att1);
// Get items in the table and write out the ID value.
ExecuteStatementResponse response = executeStatementRequest(ddb, sqlStatement, parameters);
System.out.println("ExecuteStatement successful: "+ response.toString());
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}

How do we hash/encrypt multiple columns value into aws dynamodb?

I am currently working on a payment api that will stored the values into aws dynamodb.
Currently, i have managed to link it together with aws but now i'm just wondering if there is any method such that i am able to encrypt multiple columns value (like payeeName and payeeCardNo) before storing into the aws dynamodb??
Below is my code:
package com.example.payment.model;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#AllArgsConstructor
#NoArgsConstructor
#DynamoDBTable(tableName = "CitiBank")
public class CitiBank {
#DynamoDBHashKey
#DynamoDBAutoGeneratedKey
private String paymentID;
// payment Information
#DynamoDBAttribute
private String payeeName;
#DynamoDBAttribute
private String payeeCardNo;
#DynamoDBAttribute
private int price;
public String getPaymentID(){
return paymentID;
}
public void setPaymentID(String paymentID){
this.paymentID = paymentID;
}
public String getPayeeName(){
return payeeName;
}
public void setPayeeName(String payeeName){
this.payeeName = payeeName;
}
public String getPayeeCardNo(){
return payeeCardNo;
}
public void setPayeeCardNo(String payeeCardNo){
this.payeeCardNo = payeeCardNo;
}
public int getPrice(){
return price;
}
public void setPrice(int price){
this.price = price;
}
You can use the KMS service Java API to encrypt data and then store the encrypted data in DynamoDB. Here is a Java V2 example that shows you how to encrpt/decrypt data.
https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/javav2/example_code/kms/src/main/java/com/example/kms/EncryptDataKey.java
Also - not sure that you are aware that a newer API exists for Java and AWS. You are using an older version that the AWS SDK team recommends moving away from. They recommend using the DynamoDB V2 API.
The AWS SDK for Java 2.x is a major rewrite of the version 1.x code base. It’s built on top of Java 8+ and adds several frequently requested features. These include support for non-blocking I/O and the ability to plug in a different HTTP implementation at run time.
Just tested this use case and it worked. I encrypted the data using a KMS key prior to writing it to the table.
Java V2 code that encrypts the albumTitleValue data and then adds it to the DynamoDB table:
public static void putItemInTable(DynamoDbClient ddb,
KmsClient kmsClient,
String tableName,
String key,
String keyVal,
String albumTitle,
String albumTitleValue,
String awards,
String awardVal,
String songTitle,
String songTitleVal,
String keyId ){
HashMap<String,AttributeValue> itemValues = new HashMap<String,AttributeValue>();
//Encrypt the albumTitleValue before writing it to the table
SdkBytes myBytes = SdkBytes.fromUtf8String(albumTitleValue);
EncryptRequest encryptRequest = EncryptRequest.builder()
.keyId(keyId)
.plaintext(myBytes)
.build();
EncryptResponse response = kmsClient.encrypt(encryptRequest);
// Get the encrypted data
SdkBytes encryptedData = response.ciphertextBlob();
// Add content to the table
itemValues.put(key, AttributeValue.builder().s(keyVal).build());
itemValues.put(songTitle, AttributeValue.builder().s(songTitleVal).build());
itemValues.put(albumTitle, AttributeValue.builder().bs(encryptedData).build());
itemValues.put(awards, AttributeValue.builder().s(awardVal).build());
PutItemRequest request = PutItemRequest.builder()
.tableName(tableName)
.item(itemValues)
.build();
try {
ddb.putItem(request);
System.out.println(tableName +" was successfully updated");
} catch (ResourceNotFoundException e) {
System.err.format("Error: The Amazon DynamoDB table \"%s\" can't be found.\n", tableName);
System.err.println("Be sure that it exists and that you've typed its name correctly!");
System.exit(1);
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
You can find additional DynamoDB Java V2 examples here:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/example_code/dynamodb

How to Fix Cached Schema Registry lookup causing terrible performance

Edit: I discovered this other question from a few years back (How to populate the cache in CachedSchemaRegistryClient without making a call to register a new schema?). It mentions that the CachedSchemaRegistryClient needs to register the schema to the actual registry to make it cached, and there has been no solution yet to work around this. So leaving my question here, but wanted that to be made aware as well.
I am working on a program that is pulling a byte array from kafka, decrypting it (so it is secure while on kafka), converting the bytes to a string, the json string to json object, looking up the schema from the schema registry (utilizing CachedSchemaRegistryClient), converting the json bytes to a generic record using the schema from the retrieved schema from the registry metadata, and then serializing that generic record into avro bytes.
After running some tests it seems that the CachedSchemaRegistyClient is the major performance drain. But from what I can tell this is the best way to go about getting the schema metadata. Have I implemented something poorly or is there some other way that this can be done that works with my use case?
Here is the code for what handles everything after the decrypting:
package org.apache.flink;
import avro.fullNested.FinalMessage;
import io.confluent.kafka.schemaregistry.client.CachedSchemaRegistryClient;
import io.confluent.kafka.schemaregistry.client.SchemaMetadata;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericRecord;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.util.Collector;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import serializers.AvroFinishedMessageSerializer;
import tech.allegro.schema.json2avro.converter.JsonAvroConverter;
public class JsonToAvroBytesParser implements FlatMapFunction<String, byte[]> {
private transient CachedSchemaRegistryClient schemaRegistryClient;
private transient AvroFinishedMessageSerializer avroFinishedMessageSerializer;
private String schemaUrl;
private Integer identityMaxCount;
public JsonToAvroBytesParser(String passedSchemaUrl, int passedImc){
schemaUrl = passedSchemaUrl;
identityMaxCount = passedImc;
}
private void ensureInitialized() {
if (schemaUrl.equals("")) {
schemaUrl = "https://myschemaurl.com/";
}
if(identityMaxCount == null){
identityMaxCount = 5;
}
if(schemaRegistryClient == null){
schemaRegistryClient = new CachedSchemaRegistryClient(schemaUrl, identityMaxCount);
}
if(avroFinalMessageSerializer == null){
avroFinalMessageSerializer = new AvroFinalMessageSerializer(FinalMessage.class);
}
}
#Override
public void flatMap(String s, Collector<byte[]> collector) throws Exception {
ensureInitialized();
Object obj = new JSONParser().parse(s);
JSONObject jsonObject = (JSONObject) obj;
try {
String headers = jsonObject.get("headers").toString();
JSONObject body = (JSONObject) jsonObject.get("requestBody");
if(headers != null && body != null){
String kafkaTopicFromHeaders = "hard_coded_name-value";
//NOTE: this schema lookup has serious performance issues.
SchemaMetadata schemaMetadata = schemaRegistryClient.getLatestSchemaMetadata(kafkaTopicFromHeaders);
//TODO: need to implement recovery method if schema cannot be reached.
JsonAvroConverter converter = new JsonAvroConverter();
GenericRecord specificRecord = converter.convertToGenericDataRecord(body.toJSONString().getBytes(), new Schema.Parser().parse(schemaMetadata.getSchema()));
byte[] bytesToReturn = avroFinishedMessageSerializer.serializeWithSchemaId(schemaMetadata, specificRecord);
collector.collect(bytesToReturn);
}
else {
System.out.println("json is incorrect.");
}
} catch (Exception e){
System.out.println("json conversion exception caught");
}
}
}
Thanks for any help in advance!
It appears the getLatestSchemaMetadata method does not use the cache. If you want your calls to use the cache to improve performance perhaps you can reorganize your program to use one of the other methods that does use the cache, perhaps lookup schema by ID or register schema by name with definition string.
I'm having trouble locating the documentation for Java (or Python, or C++) that confirms this is how SchemaRegistry works (tried here). But the .Net docs says at least in that client API the getLatest method is not cached.

Soap Request using axis2 java in Eclipse

I want to make an insert operation in an import set table through the web service from ServiceNow with axis2 version 1.6.4
I used wsdl2java with the wsdl file to create classes in my java project.
After it, i created a new class Request on which i would build my soap request to the webservice.
I believe I have 2 (might be more) major problems:
Unserstanding the difference between a stub and a proxy, respectively, the classes ServiceNowSoapStub and ServiceNowSoapProxy and what is the purpose of each of them.
The existing insert method needs a lot of arguments and i wish to make inserts with a selected number of arguments. do i need to add that specific insert method to the architecture?
Here is what I have:
public class Request {
public static void main(String[] args) throws RemoteException {
try{
HttpTransportProperties.Authenticator basicAuthentication = new HttpTransportProperties.Authenticator();
basicAuthentication.setUsername("xxxx");
basicAuthentication.setPassword("xxxx");
ServiceNowSoapStub proxy = new ServiceNowSoapStub();
proxy._setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);
proxy._setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, basicAuthentication);
BigDecimal actuals = new BigDecimal("0.04");
BigInteger appId = new BigInteger("3495766");
String appNonApp = "ApNon";
BigInteger productId = new BigInteger("704217");
BigInteger serviceId = new BigInteger("1537");
String serviceName = "IT";
String bpName = "BNK-RSK";
String method = "N";
String bsCode = "ITDV";
String customerCostCode = "30973250";
String customerCountry = "US";
String customerGroup = "Wealth";
String customerLegalEntity = "HB";
String dsId = "EU56";
BigInteger supplierCostCode = new BigInteger("675136");
String supplierCountry = "UK";
String supplierLegalEntity = "BR";
BigInteger total = new BigInteger ("411");
ServiceNowSoapProxy request = new ServiceNowSoapProxy("https://dev34363.service-now.com/u_it_actuals_import_set");
request.insertTest(actuals, appId, appNonApp, productId, serviceId, serviceName, bpName, method, bsCode, customerCostCode,
customerCountry, customerGroup, customerLegalEntity, dsId, supplierCostCode, supplierCountry, supplierLegalEntity, total);
} catch (org.apache.axis.AxisFault e) {
e.printStackTrace();
}
}
}
What am I doing wrong? or can please anyone refer me to some helpful link?
The wiki page of servicenow addressing this subject is a bit out of date so i can't solve my problem through it.
Thanks!

OpenID Connect - how to verify id token in Java?

I've implemented the basic OpenID connect flow in my java application and it seems to work fine.
I'd like to use an existing java library to verify the id token, as detailed here on a Salesforce page about implementing OpenId connect.
Are there any existing libraries that implement this well? I've got the response parsed, I just need to find some simple way to verify the id token is valid.
The following example will validate an id_token from an OAuth2 call for Salesforce, without any 3rd party libraries. Note that you'll have to supply a valid id_token below to test this out.
package jwt_validate_signature_sf_no_third_party;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.RSAPublicKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Main
{
// Sample id_token that needs validation. This is probably the only field you need to change to test your id_token.
// If it doesn't work, try making sure the MODULUS and EXPONENT constants are what you're using, as detailed below.
public static final String id_token = "YOUR_ID_TOKEN_HERE";
public static final String[] id_token_parts = id_token.split("\\.");
// Constants that come from the keys your token was signed with.
// Correct values can be found from using the "kid" value and looking up the "n (MODULUS)" and "e (EXPONENT)" fields
// at the following url: https://login.salesforce.com/id/keys
// MAJOR NOTE: This url will work for 90% of your use cases, but for the other 10%
// you'll need to make sure you get the "kid" value from the instance url that
// the api responses from Salesforce suggest for your token, as the kid values *will* be different.
// e.g. Some users would need to get their kid values from https://na44.salesforce.com/id/keys for example.
// The following 2 values are hard coded to work with the "kid=196" key values.
public static final String MODULUS = "5SGw1jcqyFYEZaf39RoxAhlq-hfRSOsneVtsT2k09yEQhwB2myvf3ckVAwFyBF6y0Hr1psvu1FlPzKQ9YfcQkfge4e7eeQ7uaez9mMQ8RpyAFZprq1iFCix4XQw-jKW47LAevr9w1ttZY932gFrGJ4gkf_uqutUny82vupVUETpQ6HDmIL958SxYb_-d436zi5LMlHnTxcR5TWIQGGxip-CrD7vOA3hrssYLhNGQdwVYtwI768EvwE8h4VJDgIrovoHPH1ofDQk8-oG20eEmZeWugI1K3z33fZJS-E_2p_OiDVr0EmgFMTvPTnQ75h_9vyF1qhzikJpN9P8KcEm8oGu7KJGIn8ggUY0ftqKG2KcWTaKiirFFYQ981PhLHryH18eOIxMpoh9pRXf2y7DfNTyid99ig0GUH-lzAlbKY0EV2sIuvEsIoo6G8YT2uI72xzl7sCcp41FS7oFwbUyHp_uHGiTZgN7g-18nm2TFmQ_wGB1xCwJMFzjIXq1PwEjmg3W5NBuMLSbG-aDwjeNrcD_4vfB6yg548GztQO2MpV_BuxtrZDJQm-xhJXdm4FfrJzWdwX_JN9qfsP0YU1_mxtSU_m6EKgmwFdE3Yh1WM0-kRRSk3gmNvXpiKeVduzm8I5_Jl7kwLgBw24QUVaLZn8jC2xWRk_jcBNFFLQgOf9U";
public static final String EXPONENT = "AQAB";
public static final String ID_TOKEN_HEADER = base64UrlDecode(id_token_parts[0]);
public static final String ID_TOKEN_PAYLOAD = base64UrlDecode(id_token_parts[1]);
public static final byte[] ID_TOKEN_SIGNATURE = base64UrlDecodeToBytes(id_token_parts[2]);
public static String base64UrlDecode(String input)
{
byte[] decodedBytes = base64UrlDecodeToBytes(input);
String result = new String(decodedBytes, StandardCharsets.UTF_8);
return result;
}
public static byte[] base64UrlDecodeToBytes(String input)
{
Base64 decoder = new Base64(-1, null, true);
byte[] decodedBytes = decoder.decode(input);
return decodedBytes;
}
public static void main(String args[])
{
dumpJwtInfo();
validateToken();
}
public static void dump(String data)
{
System.out.println(data);
}
public static void dumpJwtInfo()
{
dump(ID_TOKEN_HEADER);
dump(ID_TOKEN_PAYLOAD);
}
public static void validateToken()
{
PublicKey publicKey = getPublicKey(MODULUS, EXPONENT);
byte[] data = (id_token_parts[0] + "." + id_token_parts[1]).getBytes(StandardCharsets.UTF_8);
try
{
boolean isSignatureValid = verifyUsingPublicKey(data, ID_TOKEN_SIGNATURE, publicKey);
System.out.println("isSignatureValid: " + isSignatureValid);
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
}
public static PublicKey getPublicKey(String MODULUS, String EXPONENT)
{
byte[] nb = base64UrlDecodeToBytes(MODULUS);
byte[] eb = base64UrlDecodeToBytes(EXPONENT);
BigInteger n = new BigInteger(1, nb);
BigInteger e = new BigInteger(1, eb);
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(n, e);
try
{
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(rsaPublicKeySpec);
return publicKey;
}
catch (Exception ex)
{
throw new RuntimeException("Cant create public key", ex);
}
}
private static boolean verifyUsingPublicKey(byte[] data, byte[] signature, PublicKey pubKey) throws GeneralSecurityException
{
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pubKey);
sig.update(data);
return sig.verify(signature);
}
}
Note if you're not opposed to using a third party library, I'd totally suggest using this, as it works great. I couldn't use it for business reasons, but was glad to find it as it helped me understand how this process works, validated an id_token, I'm sure in a much more robust way.
Also, to be certain this request was signed by the same client, ensure the aud parameter in the payload matches your own client key given to you by Salesforce.
As part of Spring Security OAuth, the Spring team has developed a library called Spring Security JWT that allows manipulation of JWTs, including decoding and verifying tokens.
See the following helper class for example:
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-jwt/src/main/java/org/springframework/security/jwt/JwtHelper.java
The library is in version 1.0.0-RELEASE and available in the maven repo.

Categories