I am new to Spring MicroService i know how to handle session in Springboot monolithic application but can you please tell me how to handle session in microservice when we communicate with another microservice from one, and how to handle session if multiple instance of a microservice is running.
It's a complicated question and needs some extra logic to be written.
So, all methods, that may be called from one service to another should be parameterized and look like this:
public int externalCall(Session session)
{
if(sessionManager.isAliveSession(session)
{
sessionManager.touch(session);
//do some actions
}
else
{
throw new UnknownSessionException();
}
}
Then you should have one more service or module to deal with sessions. In my code I called it sessionManager.
It may possibly have such methods:
public interface SessionManager
{
/**
* to create session object in database, for example,
* with expiration date, create date etc.
*/
public void createSession();
/**
* to set the fact, that this session is
* still used and update its expiration time
*/
public void touch(Session session);
/**
* checks if session with this
* id is not expired.
*/
public boolean isAliveSession(Session session);
}
Here is an example how to call externalCall from the other service.
You will need to create class like this to perform session-based calls:
public class SessionTemplate
{
private SessionManager sessionManager;
private AtomicReference<SessionTO> session = new AtomicReference();
public <T> T execute(Callback<T> callback) {
Session session = this.getSession();
try {
return callback.doInSeance(session);
} catch (UnknownSessionException e) {
// exception may happen in externalCall method
session = this.createSession(session);
return callback.doInSeance(session);
}
}
private Session getSession() {
if (this.session.get() == null) {
synchronized(this) {
if (this.session.get() == null) {
this.session.set(this.createSession());
}
}
}
return (Session)this.session.get();
}
private Session createSession()
{
// create session in your DB
return sessionManager.createSession();
}
}
Now your remote calls will be performed like this:
public int getSmthFromRemote()
{
return sessionTemplate.execute(session -> microService.externalCall(session));
}
I'm currently having a problem using a JPA Listener to update/persist the current user updating/creating an object. Here is the JPAListener's code
private static UserSession userSession = null;//Scoped-session bean
// yes i know i'm accessing a session stored in HTTP in persistence layer
#PreUpdate
public void preUpdate(AbstractDAOAuditedEntity abstractEntity) {
abstractEntity.setModificationDate(new Date());
// use userSession here to set currentUser or system
}
#PrePersist
public void prePersist(AbstractDAOAuditedEntity abstractEntity) {
// same
}
public static void setUserSession(UserSession userSession) {
DAOEntityListener.userSession = userSession;
}
If i do it while processing an HttpRequest it works, because userSession is bound to an Http Session managed by spring.
But now i have a new usage, i'm receiving data from a JmsMessage, this mean i'm running in a thread without HttpContext, and so the listener crash when trying to use userSession.
As a really quick and really dirty fix i did the following :
boolean haveUser = true;
try {
userSession.getUser();
} catch (Exception e) {
haveUser = false;
}
if (!haveUser) {}
My question is not so about how to make it works but how i should have handle this properly, whether i'm in HttpContext or not ?
I have simple service, lets use resourceResolver and session for some logic:
#Component(immediate = true)
#Service(value = ServiceInterface.class)
public class ServiceInterfaceImpl implements ServiceInterface {
//Some fields
#Reference
private ResourceResolverFactory resolverFactory;
private void someMethod() {
ResourceResolver resourceResolver = null;
try {
resourceResolver = resolverFactory.getServiceResourceResolver(null);
Session session = resourceResolver.adaptTo(Session.class)
someMethod2(resourceResolver);
someMethod3(session);
} catch (LoginException e) {
log.error(e.getMessage(), e);
} finally {
if (resourceResolver != null && resourceResolver.isLive()) {
resourceResolver.close();
}
}
}
//Some implementation
}
Should I close the session, or it will be closed in automatically resourceResolver.close()?
The session will be automatically closed, when you close the ResourceResolver, so resourceResolver.close() is enough. You can dig into the code to find the place where this happens. If you get a session repo.loginAdministrative(), you should logout the session at the end, but this is not the recommended way to obtain a jcr session.
Currently I have a simple custom policy handler that looks like so:
protected override void Handle(AuthorizationContext context, UserPolicyRequirement requirement)
{
// authorize user against policy requirements
if (_authorizationTask.AuthorizeUserAgainstPolicy(context.User, requirement))
{
// User passed policy req's
context.Succeed(requirement);
}
}
Problem is, this authorization step takes a long time to execute, but this is required in many different areas of the website. Is there any readily available mechanisms to save/cache the results of this policy authorization so that I only need to do this once per session?
I am currently using Windows Authentication, if that helps.
If per session way does not cause any problem, you can use Session to store user data. Simple implementation is something like below:
First you need a service to get user data from any store
public interface IGetUserDataService
{
<type> GetUserData();
}
I assume that there is Session configuration(see) and IGetUserDataService implementation.
Then you need to create a middleware to handle Session
public class SessionMiddleware
{
private readonly RequestDelegate _next;
private readonly IGetUserDataService _getUserDataService;
public SessionMiddleware(RequestDelegate next, IGetUserDataService getUserDataService)
{
_next = next;
_getUserDataService = getUserDataService;
}
public async Task Invoke(HttpContext context)
{
//user data is obtained only once then is stored in Session
if (context.Session.Get("UserData") == null)
{
context.Session.Set("UserData", getUserDataService.GetData());
}
await _next.Invoke(context);
}
}
//In Startup.cs
app.UseMiddleware<SessionMiddleware>();
Finally get and use session data in handler
public class YourHandler : AuthorizationHandler<YourRequirement>
{
private readonly IHttpContextAccessor _accessor;
public YourHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
{
var userData =(<type>)_accessor.HttpContext.Session.Get("UserData");
// check
}
}
I'm trying to create a unified error handling/reporting in ASP.NET Web API 2.1 Project built on top of OWIN middleware (IIS HOST using Owin.Host.SystemWeb).
Currently I used a custom exception logger which inherits from System.Web.Http.ExceptionHandling.ExceptionLogger and uses NLog to log all exceptions as the code below:
public class NLogExceptionLogger : ExceptionLogger
{
private static readonly Logger Nlog = LogManager.GetCurrentClassLogger();
public override void Log(ExceptionLoggerContext context)
{
//Log using NLog
}
}
I want to change the response body for all API exceptions to a friendly unified response which hides all exception details using System.Web.Http.ExceptionHandling.ExceptionHandler as the code below:
public class ContentNegotiatedExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
var errorDataModel = new ErrorDataModel
{
Message = "Internal server error occurred, error has been reported!",
Details = context.Exception.Message,
ErrorReference = context.Exception.Data["ErrorReference"] != null ? context.Exception.Data["ErrorReference"].ToString() : string.Empty,
DateTime = DateTime.UtcNow
};
var response = context.Request.CreateResponse(HttpStatusCode.InternalServerError, errorDataModel);
context.Result = new ResponseMessageResult(response);
}
}
And this will return the response below for the client when an exception happens:
{
"Message": "Internal server error occurred, error has been reported!",
"Details": "Ooops!",
"ErrorReference": "56627a45d23732d2",
"DateTime": "2015-12-27T09:42:40.2982314Z"
}
Now this is working all great if any exception occurs within an Api Controller request pipeline.
But in my situation I'm using the middleware Microsoft.Owin.Security.OAuth for generating bearer tokens, and this middleware doesn't know anything about Web API exception handling, so for example if an exception has been in thrown in method ValidateClientAuthentication my NLogExceptionLogger not ContentNegotiatedExceptionHandler will know anything about this exception nor try to handle it, the sample code I used in the AuthorizationServerProvider is as the below:
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//Expcetion occurred here
int x = int.Parse("");
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName != context.Password)
{
context.SetError("invalid_credentials", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
context.Validated(identity);
}
}
So I will appreciate any guidance in implementing the below 2 issues:
1 - Create a global exception handler which handles only exceptions generated by OWIN middle wares? I followed this answer and created a middleware for exception handling purposes and registered it as the first one and I was able to log exceptions originated from "OAuthAuthorizationServerProvider", but I'm not sure if this is the optimal way to do it.
2 - Now when I implemented the logging as the in the previous step, I really have no idea how to change the response of the exception as I need to return to the client a standard JSON model for any exception happening in the "OAuthAuthorizationServerProvider". There is a related answer here I tried to depend on but it didn't work.
Here is my Startup class and the custom GlobalExceptionMiddleware I created for exception catching/logging. The missing peace is returning a unified JSON response for any exception. Any ideas will be appreciated.
public class Startup
{
public void Configuration(IAppBuilder app)
{
var httpConfig = new HttpConfiguration();
httpConfig.MapHttpAttributeRoutes();
httpConfig.Services.Replace(typeof(IExceptionHandler), new ContentNegotiatedExceptionHandler());
httpConfig.Services.Add(typeof(IExceptionLogger), new NLogExceptionLogger());
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new AuthorizationServerProvider()
};
app.Use<GlobalExceptionMiddleware>();
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseWebApi(httpConfig);
}
}
public class GlobalExceptionMiddleware : OwinMiddleware
{
public GlobalExceptionMiddleware(OwinMiddleware next)
: base(next)
{ }
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (Exception ex)
{
NLogLogger.LogError(ex, context);
}
}
}
Ok, so this was easier than anticipated, thanks for #Khalid for the heads up, I have ended up creating an owin middleware named OwinExceptionHandlerMiddleware which is dedicated for handling any exception happening in any Owin Middleware (logging it and manipulating the response before returning it to the client).
You need to register this middleware as the first one in the Startup class as the below:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var httpConfig = new HttpConfiguration();
httpConfig.MapHttpAttributeRoutes();
httpConfig.Services.Replace(typeof(IExceptionHandler), new ContentNegotiatedExceptionHandler());
httpConfig.Services.Add(typeof(IExceptionLogger), new NLogExceptionLogger());
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new AuthorizationServerProvider()
};
//Should be the first handler to handle any exception happening in OWIN middlewares
app.UseOwinExceptionHandler();
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseWebApi(httpConfig);
}
}
And the code used in the OwinExceptionHandlerMiddleware as the below:
using AppFunc = Func<IDictionary<string, object>, Task>;
public class OwinExceptionHandlerMiddleware
{
private readonly AppFunc _next;
public OwinExceptionHandlerMiddleware(AppFunc next)
{
if (next == null)
{
throw new ArgumentNullException("next");
}
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
try
{
await _next(environment);
}
catch (Exception ex)
{
try
{
var owinContext = new OwinContext(environment);
NLogLogger.LogError(ex, owinContext);
HandleException(ex, owinContext);
return;
}
catch (Exception)
{
// If there's a Exception while generating the error page, re-throw the original exception.
}
throw;
}
}
private void HandleException(Exception ex, IOwinContext context)
{
var request = context.Request;
//Build a model to represet the error for the client
var errorDataModel = NLogLogger.BuildErrorDataModel(ex);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ReasonPhrase = "Internal Server Error";
context.Response.ContentType = "application/json";
context.Response.Write(JsonConvert.SerializeObject(errorDataModel));
}
}
public static class OwinExceptionHandlerMiddlewareAppBuilderExtensions
{
public static void UseOwinExceptionHandler(this IAppBuilder app)
{
app.Use<OwinExceptionHandlerMiddleware>();
}
}
There are a few ways to do what you want:
Create middleware that is registered first, then all exceptions will bubble up to that middleware. At this point just write out your JSON out via the Response object via the OWIN context.
You can also create a wrapping middleware which wraps the Oauth middleware. In this case it will on capture errors originating from this specific code path.
Ultimately writing your JSON message is about creating it, serializing it, and writing it to the Response via the OWIN context.
It seems like you are on the right path with #1. Hope this helps, and good luck :)
The accepted answer is unnecessarily complex and doesn't inherit from OwinMiddleware class
All you need to do is this:
public class HttpLogger : OwinMiddleware
{
public HttpLogger(OwinMiddleware next) : base(next) { }
public override async Task Invoke(IOwinContext context)
{
await Next.Invoke(context);
Log(context)
}
}
Also, no need to create extension method.. it is simple enough to reference without
appBuilder.Use(typeof(HttpErrorLogger));
And if you wanna log only specific requests, you can filter on context properties:
ex:
if (context.Response.StatusCode != 200) { Log(context) }