I'm using org.springframework.security.oauth2.jwt library to write a JWT utility class which will be in charge of creating tokens and parsing claims. Now my question is how to I parse claims of my JWT correctly? There seems to far and few examples on the web on how to extract a claim using this library. Any help would be appreciate it
#Service
public class JwtUtil {
public static final String JWT_ISSUER = "test.com";
public static final long JWT_ACCESS_TOKEN_EXPIRY = 3600L;
public static final long JWT_REFRESH_TOKEN_EXPIRY = 7200L;
private JwtEncoder jwtEncoder;
private String extractExpiresAt(String token) {
// return expires at claim
}
private String createToken(String subject, Consumer<Map<String, Object>> claims) {
Instant now = Instant.now();
JwtClaimsSet claimsSet = JwtClaimsSet.builder()
.issuer(JWT_ISSUER)
.issuedAt(now)
.expiresAt(now.plusSeconds(JWT_ACCESS_TOKEN_EXPIRY))
.subject(subject)
.claims(claims)
.build();
return jwtEncoder.encode(JwtEncoderParameters.from(claimsSet)).getTokenValue();
}
private String createRefreshToken(String subject) {
Instant now = Instant.now();
JwtClaimsSet claimsSet = JwtClaimsSet.builder()
.issuer(JWT_ISSUER)
.issuedAt(now)
.expiresAt(now.plusSeconds(JWT_REFRESH_TOKEN_EXPIRY))
.subject(subject)
.build();
return jwtEncoder.encode(JwtEncoderParameters.from(claimsSet)).getTokenValue();
}
}
Related
I've been asked to generate a token depending on the username that is asking for it. Now I'm creating a token just with a single subject but I don't know how to change the subject dinamically before creating the token depending on the body of the request.
This is what I've done so far to generate a token with a single subject:
The service class:
#Component
#RequiredArgsConstructor
public class JwtService {
#Value("${issuer}")
private String issuer;
#Value("${kid}")
private String keyId;
#Value("#{'${audience}'.split(',')}")
private List<String> audiences;
#Value("#{'${subject}'.split(',')}")
private List<String> subject;
private final JwtKeyProvider jwtKeyProvider;
public String generateToken() throws JoseException {
JwtClaims claims = new JwtClaims();
claims.setIssuer(issuer);
claims.setAudience(Lists.newArrayList(audiences));
claims.setExpirationTimeMinutesInTheFuture(60);
claims.setJwtId(keyId);
claims.setIssuedAtToNow();
claims.setNotBeforeMinutesInThePast(0);
claims.setSubject(subject);
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setHeader("typ", "JWT");
jws.setKey(jwtKeyProvider.getPrivateKey());
jws.setKeyIdHeaderValue(keyId);
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
String jwt = jws.getCompactSerialization();
return jwt;
}
}
And the controller:
#RestController
#RequiredArgsConstructor
public class JWTController {
private final JwtService jwtService;
#PostMapping("/getToken")
public ResponseEntity getJwt(#RequestBody JwtRequest request) throws JoseException {
return ResponseEntity.ok(
JwtResponse.builder()
.token(jwtService.generateToken())
.build()
);
}
}
I could do it doing like this:
#PostMapping("/getToken")
public ResponseEntity getJwt(#RequestBody JwtRequest request) throws JoseException {
return ResponseEntity.ok(
JwtResponse.builder()
.token(jwtService.generateToken(request.getUsername()))
.build()
);
}
}
But I don't want to send any parameters in the generateToken function as I would have to change a lot of code then.
To resume I want to assign to the subject the value of the username that is sent in the body. So is there a way in the JwtService class to receive that username and set as the subject after?
Thanks in advance!
First you need to put whitelist=user1,user2 in your application.properties, because sometimes names might trigger as system variables (for example username does)
Then in JWTController you need to check if not user equals, but contains in list
#Value("#{'${whitelist}'.split(',')}")
private List<String> whitelist;
#PostMapping("/getToken")
public ResponseEntity<?> getJwt(#RequestBody JwtRequest request) throws JoseException {
if(whitelist.contains(request.username())) {
return ResponseEntity.ok(
JwtResponse.builder()
.token(jwtService.generateToken(request.username()))
.build()
);
} else {
return ResponseEntity.ok("Invalid username");
}
}
In your JWTService you need to set JWT Subject to username which passed through whitelist
claims.setSubject(username);
And finally you need to do JSON request to server
{
"username": "user2"
}
im new with retrofit and now, when i know how to sent the normal data without any objects, just with parameters or simple body i want to know how to sent the objects...
I spent like 20h to debug it and i'm confused because i dont know how to do this...
There is my codes:
API Interface:
#POST("/api/assortment")
Call<PostAssortment> getAssortment(#Body String PostShipmentProgress);
PostAssortment class:
public class PostAssortment {
private String JSON;
#SerializedName("token")
#Expose
private String token;
#SerializedName("assortment")
#Expose
private Assortment assortment;
#SerializedName("tokens")
#Expose
private List<String> tokens;
#SerializedName("positions")
#Expose
private List<Position> positions;
#SerializedName("deviceId")
#Expose
private String deviceId;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Shipment getAssortment() {
return assortment;
}
public void setAssortment(Assortment assortment) {
this.assortment = assortment;
}
public List<String> getTokens() {
return tokens;
}
public void setTokens(List<String> tokens) {
this.tokens = tokens;
}
public List<Position> getPositions() {
return positions;
}
public void setPositions(List<Position> positions) {
this.positions = positions;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getJSON() {
return JSON;
}
public void setJSON(String JSON) {
this.JSON = JSON;
}
}
And the mainJava class:
Gson gson = new Gson();
PostAssortment postAssortment= new PostAssortment();
List<String> tokens = new ArrayList<>();
tokens.add("someToken");
postAssortment.setTokens(tokens);
postAssortment.setDeviceId("aaaaa");
List<Position> currentPosition = new ArrayList<>();
Position cp = new Position();
cp.setItemName("Some");
cp.setPlace("POLAND");
cp.setTimestamp("2020-12-09T11:00:00");
currentPosition.add(cp);
postAssortment.setPositions(currentPosition);
String postAssortmentJSON = gson.toJson(postAssortment);
Call<PostAssortment> call = ApiLoginInterface.getAssortment(postAssortmentJSON);
call.enqueue(new Callback<PostAssortment>() {
#Override
public void onResponse(Call<PostAssortment> call, Response<PostAssortment> response) {
PostAssortment assortmentResponse = response.body();
}
#Override
public void onFailure(Call<PostAssortment> call, Throwable t) {
Log.d("FAILURE", "onFailure: " + t.getMessage());
}
});
}
And my retrofit onCreate:
Gson gson = new GsonBuilder()
.setLenient()
.create();
String BASE_URL = getString(API_URL);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
ApiLoginInterface = retrofit.create(ApiLoginInterface.class);
And after im trying to call it im not getting any point on call enqueue just a
Android: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
Error...
Can someone describe this and help me to make it work? :/
You haven't provided enough information to help identify the error. Probably add the full stacktrace to the question as well. But if your API post request is expecting a json body I would start with the fixes below:
Remove this:
String postAssortmentJSON = gson.toJson(postAssortment);
Then pass your object as a pojo to your retrofit interface like this:
#POST("/api/assortment")
Call<PostAssortment> getAssortment(#Body PostAssortment postAssortment);
Then when doing your call you don't need to convert it to a string json string. The adapter does that for you:
Call<PostAssortment> call = ApiLoginInterface.getAssortment(postAssortment);
Post assortment in my problem will be in a list, so to make it works I need to change the Call to
I'm trying to test getting parameters for processing a request using the Post method
#RestController
#RequestMapping("api")
public class InnerRestController {
…
#PostMapping("createList")
public ItemListId createList(#RequestParam String strListId,
#RequestParam String strDate) {
…
return null;
}
}
test method
variant 1
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class InnerRestControllerTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
void innerCreatePublishList() {
String url = "http://localhost:" + this.port;
String uri = "/api/createList";
String listStr = "kl";
String strDate = "10:21";
URI uriToEndpoint = UriComponentsBuilder
.fromHttpUrl(url)
.path(uri)
.queryParam("strListId", listStr)
.queryParam("strDate ", strDate)
.build()
.encode()
.toUri();
ResponseEntity< ItemListId > listIdResponseEntity =
restTemplate.postForEntity(uri, uriToEndpoint, ItemListId.class);
}
}
variant 2
#Test
void createList() {
String uri = "/api/createList";
String listStr = "kl";
String strDate = "10:21";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(uri)
.queryParam("strListId", listStr)
.queryParam("strDate ", strDate);
Map<String, String> map = new HashMap<>();
map.put("strListId", listStr);//request parameters
map.put("strDate", strDate);
ResponseEntity< ItemListId > listIdResponseEntity =
restTemplate.postForEntity(uri, map, ItemListId.class);
}
Update_1
In my project exceptions is handled thus:
dto
public final class ErrorResponseDto {
private String errorMsg;
private int status;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
LocalDateTime timestamp;
...
handler
#RestControllerAdvice
public class ExceptionAdviceHandler {
#ExceptionHandler(value = PublishListException.class)
public ResponseEntity<ErrorResponseDto> handleGenericPublishListDublicateException(PublishListException e) {
ErrorResponseDto error = new ErrorResponseDto(e.getMessage());
error.setTimestamp(LocalDateTime.now());
error.setStatus((HttpStatus.CONFLICT.value()));
return new ResponseEntity<>(error, HttpStatus.CONFLICT);
}
}
In methods, where necessary, I throw a specific exception...
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved
[org.springframework.web.bind.MissingServletRequestParameterException:
Required String parameter 'strListId' is not present]
Who knows what the error is. Please explain what you need to add here and why ?
Let's take a look on declarations of postEntity:
postForEntity(URI url, Object request, Class<T> responseType)
...
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
As you can see, first argument is either URI or String with uriVariables, but second argument is always request entity.
In you first variant you put uri String as URI and then pass uriToEndpoint as request entity, pretending that it is request object. Correct solution will be:
ResponseEntity<ItemListId> listIdResponseEntity =
restTemplate.postForEntity(uriToEndpoint, null, ItemListId.class);
Addressing your comments.
If server responded with HTTP 409, RestTemplate will throw exception with content of your ErrorResponseDto. You can catch RestClientResponseException and deserialize server response stored in exception. Something like this:
try {
ResponseEntity<ItemListId> listIdResponseEntity =
restTemplate.postForEntity(uriToEndpoint, null,
ItemListId.class);
...
} catch(RestClientResponseException e) {
byte[] errorResponseDtoByteArray = e.getResponseBodyAsByteArray();
// Deserialize byte[] array using Jackson
}
I am making an Android app that connects to a web service via REST API and I have a dilemma with the design of the internal architecture.
Now I have class Client.java whose purpouse is make connect with the server (ConnectionMethod is Enum that contains GET|POST values):
public class Client {
private AsyncHttpClient client = new AsyncHttpClient(); //I use com.loopj.AsyncHttpClient to connect
private ConnectionMethod method;
private RequestParams params = new RequestParams();
private AsyncHttpResponseHandler responseHandler = new JsonHttpResponseHandler(){
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
//Actions when connection success
}
#Override
public void onFailure(int statusCode, Header[] headers, JSONObject response, Throwable error) {
//Actions when connection fails
}
};
public Client (RequestParams params, ConnectionMethod method) {
this.params = params;
this.method = method;
}
public void addParameters (Map<String, String> parameters) {
for (Map.Entry<String, String> entry : parameters.entrySet()) {
this.params.put(entry.getKey(), entry.getValue());
}
}
public ServerResponse connect () {
RequestHandle handle;
if (this.method==ConnectionMethod.POST) {
handle = postRequest();
}
else {
handle = getRequest();
}
//How can I treat here different type of responses homogeneously?
}
private RequestHandle getRequest () {
return client.get(Constants.getEndpoint(), this.params, this.responseHandler);
}
private RequestHandle postRequest () {
return client.post(Constants.getEndpoint(), this.params, this.responseHandler);
}
}
A sample method that requests info from the server is this:
public static void login (String login, String password) {
//This classes should be static or dynamic?
Map<String, String> map = new HashMap<String, String>();
map.put("login", login);
map.put("password", password);
map.put("method", "site_login");
Client c = new Client();
c.addParameters(map);
c.getRequest();
}
All server responses are JSON: {status:0, result:array/int/string} when response is correct and {status:-1, message:string} when response is incorrect.
Additionaly I want to make classes to model components from JSON result (User.java, Message.java...) and intermediate methods between UI and API to implement the logic of the app and classes.
What is the best way to design an homogeneous connection system that manages automatically correct/fail response and independent of model (user, message...)?
There is a bunch of frameworks which can make this whole process much easier.
For example Retrofit is very simple framework for mapping java classes to REST calls. It comes with gson which will automatically deserialize response from json to plain java objects.
It also allows use callbacks as well as rxJava Observables. It allows to handle errors as well.
You can check sample app: https://github.com/JakeWharton/u2020
You are describing tools that already exist. My favorite happens to be Retrofit but there are others out there. Retrofit can handle the success and fail responses and even map JSON directly to a POJO.
My API client
public class ApiClient {
private static ApiInterface sApiInterface;
public static ApiInterface getApiClient(Context context) {
//build the rest adapter
if (sApiInterface == null) {
final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("example.com")
.build();
sApiInterface = restAdapter.create(ApiInterface.class);
}
return sApiInterface;
}
public interface ApiInterface {
#GET("/program/{id}")
void getProgram(#Path("id") int id, RetrofitCallback<Program> callback);
}
My RetrofitCallback
public class RetrofitCallback<S> implements Callback<S> {
private static final String TAG = RetrofitCallback.class.getSimpleName();
#Override
public void success(S s, Response response) {
}
#Override
public void failure(RetrofitError error) {
Log.e(TAG, "Failed to make http request for: " + error.getUrl());
Response errorResponse = error.getResponse();
if (errorResponse != null) {
Log.e(TAG, errorResponse.getReason());
if (errorResponse.getStatus() == 500) {
Log.e(TAG, "Handle Server Errors Here");
}
}
}
}
My model
public class Program {
#Expose
private doublea.models.Airtime Airtime;
#Expose
private String id;
#Expose
private String title;
#SerializedName("short_name")
#Expose
private String shortName;
#SerializedName("full_description")
#Expose
private String fullDescription;
#SerializedName("short_description")
#Expose
private String shortDescription;
#Expose
private doublea.models.Image Image;
#SerializedName("image")
#Expose
private String imageName;
#Expose
private List<Host> hosts = new ArrayList<Host>();
#Expose
private List<Category> categories = new ArrayList<Category>();
#Expose
private List<Airtime> airtimes = new ArrayList<Airtime>();
/** Getters and Setters */
public Program() {
}
How it is used.
private void executeProgramApiCall(int programId) {
ApiClient.getApiClient(this).getProgram(programId, new RetrofitCallback<Program>() {
#Override
public void success(Program program, Response response) {
super.success(program, response);
addDataToAdapter(program);
}
});
}
I just implemented Google translator toolkit API using google-api-java-client library.
The problem is, that I can authenticate using clientLogin with the old "gdata" client library, but I can't manage to do that with google-api-java-client.
It's quite straightforward, but I'm still getting 403 forbidden response. The requests (old / new) are almost the same, but only the auth tokens differ. Google just sends me a token that I cannot authenticate with...
Please anybody help, I spent an hour with the entire model implementation and then 3 hours of this hell.
public class GttClient {
public static void main(String[] args) {
Debug.enableLogging();
HttpTransport transport = setUpTransport();
try {
authenticateWithClientLogin(transport);
printResults(executeGet(transport, GttUrl.forDocuments()));
} catch (IOException e) {
e.printStackTrace();
}
}
private static HttpTransport setUpTransport() {
HttpTransport transport = GoogleTransport.create();
GoogleHeaders headers = (GoogleHeaders) transport.defaultHeaders;
headers.setApplicationName("Google-PredictionSample/1.0");
headers.gdataVersion = "2.0";
AtomParser parser = new AtomParser();
parser.namespaceDictionary = Namespace.DICTIONARY;
transport.addParser(parser);
return transport;
}
private static void authenticateWithClientLogin(HttpTransport transport)
throws IOException {
ClientLogin clientLogin = new ClientLogin();
clientLogin.authTokenType = "gtrans";
clientLogin.accountType = "HOSTED_OR_GOOGLE";
clientLogin.username = "user#gmail.com";
clientLogin.password = "password";
clientLogin.authenticate().setAuthorizationHeader(transport);
}
public static Feed executeGet(HttpTransport transport, GttUrl url)
throws IOException {
HttpRequest request = transport.buildGetRequest();
// url.fields = GData.getFieldsFor(Feed.class);
request.url = url;
return request.execute().parseAs(Feed.class);
}
}
public class GttUrl extends GoogleUrl {
static final String ROOT_URL = "https://translate.google.com/toolkit/feeds";
#Key("sharedwith")
public String sharedwith;
#Key("onlydeleted")
public String onlydeleted;
#Key("scope")
public String scope;
public GttUrl(String url) {
super(url);
if (Debug.ENABLED) {
this.prettyprint = true;
}
}
public static GttUrl forRoot() {
return new GttUrl(ROOT_URL);
}
public static GttUrl forDocuments() {
GttUrl result = forRoot();
result.pathParts.add("documents");
return result;
}
public static GttUrl forTranslMemories() {
GttUrl result = forRoot();
result.pathParts.add("tm");
return result;
}
public static GttUrl forGlossaries() {
GttUrl result = forRoot();
result.pathParts.add("glossary");
return result;
}
}
So, I implemented translator toolkit api in an hour and then I got
stuck for 4 hours on clientLogin authorization....
the proper setup of the request is
gdataVersion = "1.0";
and GET request
Unfortunately during the course of trying I had either
1.0 and POST
or
2.0 and GET
It means that gdataVersion = "2"; is working only for APIs for which the "new" client is already implemented...afaik