there are numerous posts explaining how to add dynamic header into request using Feign Interceptor
#Bean
public RequestInterceptor requestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
var jwtToken = refreshAccessTokenClient.refresh();
requestTemplate.header("Authorization", jwtToken);
}
}
is there any possibility to share jwtToken across many threads and refresh in periodically? Current solution is sub-optimal and makes 1 extra call each time.
I came up with solution:
#Bean
public RequestInterceptor requestInterceptor(#Value("${service.login}") String login,
#Value("${service.apiKey}") String apiKey) {
return
requestTemplate -> {
var request = LoginRequest.builder().loginId(login).apiKey(apiKey).build();
var jwtToken = authAuthProvider.login(request).getData().getAccessToken();
requestTemplate.header("Authorization", "Bearer " + jwtToken);
};
}
I blindly request jwtToken for every API call. It doesn't look like effective solution. Should look for some #Cacheable thing with TLL?
Related
I am trying to use JWT in my API, and configuration is completed, can use postman tool to access data from it. However when I use Blazor as front end to access it , the request doesn't have token, so always give a 401 code.
Below is my Blazor code.
program.cs
builder.Services.AddHttpClient<IOptionService, OptionService> ("OptionAPI", (sp, cl) => {
cl.BaseAddress = new Uri("https://localhost:7172");
});
builder.Services.AddScoped(
sp => sp.GetService<IHttpClientFactory>().CreateClient("OptionAPI"));
OptionService.cs
public class OptionService : IOptionService {
private readonly HttpClient _httpClient;
public OptionService(HttpClient httpClient) {
_httpClient = httpClient;
}
public async Task<IEnumerable<OptionOutputDto>> GetOptionsAsync(Guid quizId, Guid questionId) {
_httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", "token");
return await JsonSerializer.DeserializeAsync<IEnumerable<OptionOutputDto>>(
await _httpClient.GetStreamAsync($"api/quizzes/{quizId}/{questionId}/options"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}
I tired use " new AuthenticationHeaderValue("Bearer", "token");" to attach token in header, but its not working, still give 401 code.
And I also tried use
private readonly IHttpClientFactory _httpClient;
public OptionService(IHttpClientFactory httpClient) {
_httpClient = httpClient;
}
public async Task<IEnumerable<OptionOutputDto>> GetOptionsAsync(Guid quizId, Guid questionId) {
var newHttpClient = _httpClient.CreateClient();
newHttpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", "token");
return await JsonSerializer.DeserializeAsync<IEnumerable<OptionOutputDto>>(
await newHttpClient.GetStreamAsync($"api/quizzes/{quizId}/{questionId}/options"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}
it's also not working, give me an error,
Unhandled exception rendering component: A suitable constructor for type 'Services.OptionService' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
System.InvalidOperationException: A suitable constructor for type .....
Can anyone has a simple way to attach token in request header?
Thanks in advance.
I think the good option is :
builder.Services.AddHttpClient<IOptionService, OptionService> ("OptionAPI", (sp, cl) => {
cl.BaseAddress = new Uri("https://localhost:7172");
});
Could you check if the token is present in header or not?
Your error is most likely related to how the OptionService is being registered in dependency injection. It either needs an empty constructor adding - and/or - you need to ensure that the constructor has all of its dependencies registered correctly in the ServicesCollection too.
The exception is quite explicit:
Ensure the type is concrete and all parameters of a public constructor
are either registered as services or passed as arguments. Also ensure
no extraneous arguments are provided
I gave a similar answer here. Basically you need to include the BaseAddressAuthorizationMessageHandler when defining your httpclients. If you're using a typed httpclient, you can inject the IAccessTokenProvider and get the token from there. Kinda like this:
public class MyHttpClient(IAccessTokenProvider tokenProvider, HttpClient httpClient)
{
_tokenProvider = tokenProvider;
_httpClient = httpClient;
}
private async Task RequestAuthToken()
{
var requestToken = await _tokenProvider.RequestAccessToken();
requestToken.TryGetToken(out var token);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Value);
}
public async Task<IEnumerable<ReplyDto>> SendHttpRequest()
{
await RequestAuthToken();
return await JsonSerializer.DeserializeAsync<IEnumerable<ReplyDto>>(
await _httpClient.GetStreamAsync("api/getendpoint"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}
I have a Vert.x REST service that receive requests with jwt tokens, and I want to call my another REST service passing received token. Between router handler and WebClient call I have a business logic layer. My question is if there is a method to provide token to webClient other than passing it explicitly through my business logic layer? In other words is it possible to retrieve somehow my RoutingContext and token from e.g. vertxContext or an other component?
Example code demonstrating what I would like to achieve:
Verticle cass
public class RestApiVerticle extends AbstractVerticle {
businessLogicService service;
#Override
public void start() throws Exception {
initService();
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
JWTAuth authProvider = JWTAuth.create(vertx, getAuthConfig());
router.route("/*").handler(JWTAuthHandler.create(authProvider));
router.route("/somePath").handler(this::handleRequest);
server.requestHandler(router::accept).listen(config().getInteger("port"));
}
private void handleRequest(RoutingContext context){
service.doSomeBusinessLogic(); //I could pass context here, but I thing this is not a proper way to do it, as business logic should not know about RequestContext
}
private void initService(){
ExternalAPICaller caller = new ExternalAPICaller(WebClient.create(vertx));
service = new BusinessLogicService(caller);
}
private JsonObject getAuthConfig() {
return new JsonObject();
}
}
BusinessLogicService:
public class BusinessLogicService {
ExternalAPICaller caller;
public BusinessLogicService(ExternalAPICaller caller){
this.caller = caller;
}
public void doSomeBusinessLogic(){
caller.doSth();
}
}
ExternalAPICaller:
public class ExternalAPICaller {
WebClient client;
public ExternalAPICaller(WebClient client){
this.client = client;
}
public void doSth(){
String TOKEN = null; // I would like to retrive here my token from some vertx component
client.post("externalAPIpath")
.putHeader("Authorization", "Bearer" + TOKEN)
.send(ctx -> {
//(..)
});
}
}
My implementation is in JavaScript (Node.js/Express), but I used cookies to send the JWT to the client.
res.cookie("auth", token);
return res.redirect(`http://localhost:3000/socialauthredirect`);
When you call your do business logic method you could pass the request authorization header value as it contains your untouched jwt token. Then on your web client add a header with that value and of course named authorization and your token is forwarded to the next service.
I have a REST endpoint implemented with Spring MVC #RestController. Sometime, depends on input parameters in my controller I need to send http redirect on client.
Is it possible with Spring MVC #RestController and if so, could you please show an example ?
Add an HttpServletResponse parameter to your Handler Method then call response.sendRedirect("some-url");
Something like:
#RestController
public class FooController {
#RequestMapping("/foo")
void handleFoo(HttpServletResponse response) throws IOException {
response.sendRedirect("some-url");
}
}
To avoid any direct dependency on HttpServletRequest or HttpServletResponse I suggest a "pure Spring" implementation returning a ResponseEntity like this:
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(newUrl));
return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY);
If your method always returns a redirect, use ResponseEntity<Void>, otherwise whatever is returned normally as generic type.
Came across this question and was surprised that no-one mentioned RedirectView. I have just tested it, and you can solve this in a clean 100% spring way with:
#RestController
public class FooController {
#RequestMapping("/foo")
public RedirectView handleFoo() {
return new RedirectView("some-url");
}
}
redirect means http code 302, which means Found in springMVC.
Here is an util method, which could be placed in some kind of BaseController:
protected ResponseEntity found(HttpServletResponse response, String url) throws IOException { // 302, found, redirect,
response.sendRedirect(url);
return null;
}
But sometimes might want to return http code 301 instead, which means moved permanently.
In that case, here is the util method:
protected ResponseEntity movedPermanently(HttpServletResponse response, String url) { // 301, moved permanently,
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, url).build();
}
As the redirections are usually needed in a not-straightforward path, I think throwing an exception and handling it later is my favourite solution.
Using a ControllerAdvice
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {
NotLoggedInException.class
})
protected ResponseEntity<Object> handleNotLoggedIn(
final NotLoggedInException ex, final WebRequest request
) {
final String bodyOfResponse = ex.getMessage();
final HttpHeaders headers = new HttpHeaders();
headers.add("Location", ex.getRedirectUri());
return handleExceptionInternal(
ex, bodyOfResponse,
headers, HttpStatus.FOUND, request
);
}
}
The exception class in my case:
#Getter
public class NotLoggedInException extends RuntimeException {
private static final long serialVersionUID = -4900004519786666447L;
String redirectUri;
public NotLoggedInException(final String message, final String uri) {
super(message);
redirectUri = uri;
}
}
And I trigger it like this:
if (null == remoteUser)
throw new NotLoggedInException("please log in", LOGIN_URL);
if you #RestController returns an String you can use something like this
return "redirect:/other/controller/";
and this kind of redirect is only for GET request, if you want to use other type of request use HttpServletResponse
I'm trying to design my first public API, and I'm trying to learn how REST works with authentication, especially in the context of completely client-side apps using js-frameworks, e.g., angularJS.
Say you have a client which is a browser application (i.e., HTML, JS, CSS only) served as static files from something like nginx using a javascript framework to consume a REST service from, e.g. something that requires a secret access key that's used to create a signature for each request to the service, something like Amazon S3.
In terms of authentication in this scenario, where you don't have a server-side application, how would the secret access key be handled, i.e., how do you get it, where do you store it, etc.? It would seem like a horrible security situation to serve the key for each request (even if it only happens once to bootstrap the application).
And even if you do have a light server-side application--how do you securely inform the client (which still calls the authenticated 3rd party API itself) what the signature should be for every request it could possibly make? I'm very confused by how this is supposed to be designed from either end.
I've done a few AngularJS apps and the way that I've found is to use an HttpModule like this one:
using System;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web;
namespace YourSolution.WebApp.Modules
{
public class BasicAuthenticationHttpModule : IHttpModule
{
public BasicAuthenticationHttpModule()
{
}
public void Init(HttpApplication context)
{
context.AuthenticateRequest += OnApplicationAuthenticateRequest;
context.EndRequest += OnApplicationEndRequest;
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
private static bool CheckPassword(
string username, string password)
{
return username == password;
}
private static void AuthenticateUser(string credentials)
{
try
{
var encoding = Encoding.GetEncoding(
"iso-8859-1");
credentials = encoding.GetString(
Convert.FromBase64String(credentials));
var separator = credentials.IndexOf(':');
var name = credentials.Substring(0, separator);
var password = credentials.Substring(separator + 1);
var validated = CheckPassword(name, password);
if (!validated) return;
var identity = new GenericIdentity(name);
SetPrincipal(new GenericPrincipal(identity, null));
}
catch (FormatException)
{
}
}
private static void OnApplicationAuthenticateRequest(
object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
var authHeader = request.Headers["Authorization"];
if (authHeader == null) return;
var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
if (authHeaderVal.Scheme.Equals(
"basic",
StringComparison.OrdinalIgnoreCase)
&& authHeaderVal.Parameter != null)
{
AuthenticateUser(authHeaderVal.Parameter);
}
}
private static void OnApplicationEndRequest(
object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
//response.Headers.Add(
// "WWW-Authenticate",
// string.Format("Basic realm=\"{0}\"", Realm));
}
}
public void Dispose()
{
}
}
}
The most important part is inside CheckPassword method, there is where you should validate the credentials.
Another point is this line response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", Realm)); if you don't comment this line, the classic login requested form will show up, and if you do comment this line you have to catch the 401 error in your requests.
If you want to know about realm: What is the “realm” in basic authentication.
Plus, you will need to register the module in your web.config file:
<system.webServer>
<modules>
<add
name="BasicAuthenticationHttpModule"
type="YourSolution.WebApp.Modules.BasicAuthenticationHttpModule" />
</modules>
</system.webServer>
Then I've added these two methods to deal with the authentication token:
// u: username; p: password
CreateBasicAuthenticationToken = function (u, p) {
var t = u + ':' + p;
var hat = btoa(t);
window.sessionStorage.setItem('basicauthtoken', 'basic ' + hat);
};
DestroyBasicAuthenticationToken = function () {
window.sessionStorage.removeItem('basicauthtoken');
};
The btoa method: The btoa() method of window object is used to convert a given string to a encoded data (using base-64 encoding) string.. Taken from: http://www.w3resource.com/javascript/client-object-property-method/window-btoa.php.
And last I've added the authtoken to the request header using the beforeSend:
$.ajax({
type: 'GET',
url: 'your url',
beforeSend: function (xhr) {
window.sessionStorage.getItem('basicauthtoken');
}
}).done(function (data, textStatus, xhr) {
//...
});
Please do note using jQuery outside an angular directive is not recommended, AngularJS best practices dictates jQuery code must be always placed inside a directive.
Hope it helps.
I'm creating a small REST web service using Netbeans. This is my code:
private UriInfo context;
private String name;
public GenericResource() {
}
#GET
#Produces("text/html")
public String getHtml() {
//TODO return proper representation object
return "Hello "+ name;
}
#PUT
#Consumes("text/html")
public void putHtml(String name) {
this.name = name;
}
I'm calling the get method ok since when I call http://localhost:8080/RestWebApp/resources/greeting I get "Hello null" but I'm trying to pass a parameter using http://localhost:8080/RestWebApp/resources/greeting?name=Krt_Malta but the PUT method is not being called... Is this the correct way to pass a parameter or am I missing something?
I'm a newbie to Rest bdw, so sry if it's a simple question.
Thanks! :)
Krt_Malta
The second URL is a plain GET request. To pass data to a PUT request you have to pass it using a form. The URL is reserved for GET as far as I know.
If you build the HTTP-header yourself, you must use POST instead of GET:
GET /RestWebApp/resources/greeting?name=Krt_Malta HTTP/1.0
versus
POST /RestWebApp/resources/greeting?name=Krt_Malta HTTP/1.0
If you use a HTML-form, you must set the method-attribute to "PUT":
<form action="/RestWebApp/resources/greeting" method="PUT">
For JAX-RS to mactch a method annotated with #PUT, you need to submit a PUT request. Normal browsers don't do this but cURL or a HTTP client library can be used.
To map a query parameter to a method argument, JAX-RS provides the #QueryParam annotation.
public void putWithQueryParam(#QueryParam("name") String name) {
// do something
}
You can set:
#PUT
#path{/putHtm}
#Consumes("text/html")
public void putHtml(String name) {
this.name = name;
}
and if you use something like google`s Volley library you can do.
GsonRequest<String> asdf = new GsonRequest<String>(ConnectionProperties.happyhourURL + "/putHtm", String.class, yourString!!, true,
new Response.Listener<Chain>() {
#Override
public void onResponse(Chain response) {
}
}, new CustomErrorListener(this));
MyApplication.getInstance().addToRequestQueue(asdf);
and GsonRequest will look like:
public GsonRequest(String url, Class<T> _clazz, T object, boolean needLogin, Listener<T> successListener, Response.ErrorListener errorlistener) {
super(Method.PUT, url, errorlistener);
_headers = new HashMap<String, String>();
this._clazz = _clazz;
this.successListener = successListener;
this.needsLogin = needLogin;
_object = object;
setTimeout();
}