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));
}
Related
I have a question regarding Apache Shiro.
I´m using permission and role concept.
I have on the left side a menu with many links to my other pages (create an employee, show employee list etc.).
For each menu item I have also security for it to hide it or not (depends on the permission), like:
<pm:menu widgetVar="me"
rendered="#{checkPermissionController.checkPermission(['myprofile:show', 'myprofile:edit'])}">
To check if the user is permitted or not, I have those two functions in my bean:
/**
* Check permission for login User
*
* #return
* #throws IOException
* #throws PermissionGroupNotFoundException
*/
public boolean checkPermission(String permissionName) throws IOException {
if (loginBean.getCurrentUserShiro().isPermitted(permissionName)) {
return true;
}
return false;
}
/**
* If one of the permission is true
*
* #param strings
* #return
*/
public boolean checkPermission(List<String> list) {
int i = list.size();
if (i != 0) {
for (String s : list) {
if (loginBean.getCurrentUserShiro().isPermitted(s)) {
return true;
}
}
}
return false;
}
My question is now more against performance.
Is Apache Shiro execute for each menu entry a request against the database if the user is permitted or not?
Or does Shiro fetch at login time all permission for a user and "hold" it in the "Shiro User" object?
If yes: how can I improve it?
Edit:
Here my LoginBean:
#SessionScoped
#Named
public class LoginBean implements Serializable {
private Subject currentUserShiro;
public String submit() {
LOGGER.info("START submit");
try {
currentUserShiro = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
....
}
////////////////////
// Getter + Setter
////////////////////
public String getUsername() {
return username;
}
public Subject getCurrentUserShiro() {
return currentUserShiro;
}
currentUserShiro.login(token);
I'd recommend using Shiro's filters to handle the actual login logic for you.
https://shiro.apache.org/web.html
Otherwise, you could end up with the Subject not tied to your Session. It looks like your application might be forcing a log in any time getCurrentUserShiro is called. You should let the framework handle this for you.
It's not a JSF, but you can see a basic JSP example here (the Shrio config logic will be the same):
https://github.com/apache/shiro/tree/master/samples/servlet-plugin
You would likely just replace the login page: https://github.com/apache/shiro/blob/master/samples/servlet-plugin/src/main/webapp/login.jsp with your custom page
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 am using Vert.x in backend and AngularJS for my frontend.
Vert.x server receives HTTP actions using both POST and GET methods. Somehow I am getting different session ids for each request.
Following is the code snippet from my LoginFormHandler class handle routine.
authProvider.authenticate(authInfo, res -> {
if (res.succeeded()) {
Session session = context.session();
io.vertx.ext.auth.User user = res.result();
session.put("user", user);
req.response().setStatusCode(204).end("user Login success");
//...
}
//...
}
I am putting user object inside the current session. Then I move to new page and send a POST request to the Vert.x server. Inside that POST handler, I am trying to get the session object:
Session session = context.session();
io.vertx.ext.auth.User user = session.get("user");
I am not getting the user. Also when I print session ID, I get different values for both sessions.
I have following code in start routine for the thread.
router.route().handler(CookieHandler.create());
router.route().handler(
SessionHandler.create(LocalSessionStore.create(vertx)));
AuthProvider ap = new MyAuthProvier();
router.route().handler(UserSessionHandler.create(ap));
AuthHandler basicAuthHandler = BasicAuthHandler.create(ap);
router.route("/Services/rest/user/auth").handler(MyFormLoginHandler.create(ap));
router.route("/Services/*").handler(basicAuthHandler);
Try using a new handler for authentication.
router.route("/Services/rest/user/auth").handler(new MyFormLoginHandler());
router.route("/Services/*").handler(basicAuthHandler);
Implement the handler as below
class MyFormLoginHandler implements Handler<RoutingContext> {
public void handle(RoutingContext routingContext) {
HttpServerResponse response = routingContext.response();
Session session = routingContext.session();
routingContext.request().bodyHandler(new Handler<Buffer>() {
public void handle(Buffer buf)
{
....
for (User u : users){
if (u.getPassword().equals(passwd)){
session.put("user", u.getUserName());
response.setStatusCode(204).end("User Authenticated");
break;
}
}
};
});
}
}
Access the user from session.
class TestRequest implements Handler<RoutingContext> {
public void handle(RoutingContext routingContext) {
Session session = routingContext.session();
routingContext.request().bodyHandler(new Handler<Buffer>() {
public void handle(Buffer buf) {
.....
String userName = session.get("user");
.....
};
});
}
}