Re: wicket-ki-security

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Re: wicket-ki-security

Les Hazlewood-2
Hi Tauren,

I'm forwarding this to the Shiro user list as the answer could benefit other people as well.

The User object itself should probably never be stored in the Subject or its Session - just the ID as you've pointed out.  This is definitely the appropriate way to use Shiro for most enterprise applications.

The reason is that, especially with JPA/Hibernate applications, If Shiro stored the User object directly, it would bypass lots of optimizations that are in place in persistence frameworks, such as caching.

That is, you should almost always lookup the User object during a transaction via the Subject-accessible user ID, and rely on a nice caching framework to improve performance if you don't want an expensive 'round trip' to the database each time you do the lookup. 

This is especially important for coherency - if you cache a User object in the Subject (or the Subject's Session), and you change that User object somewhere else in the application (say, via an 'update account' web page), well, now the User object in your session is 'stale' and does not reflect the one changed by visiting the 'update account' web page.

This beneficial technique is not unique to just Shiro of course - most session values should usually be extremely lightweight objects - ids or lookup keys to pull the corresponding 'real' object via a Service or DAO call, relying on the underlying persistence framework and caching to help with performance, data versioning, coherency, etc.

So a good practice is to have a 'utility' helper component that can do this translation automatically.

For example, UserService#getCurrentUser():

public User getCurrentUser() {
    Long id = (Long)SecurityUtils.getSubject().getPrincipal();
    if ( id != null ) {
        // they are either authenticated or remembered from a previous session,
        // so return the user:

        return getUser(id);
    } else {
        //not logged in or remembered:
        return null;
    }
}

I've taken this concept a little further and I wrap all my SecurityUtils.getSubject().* method operations by creating a SubjectService interface:

public interface SubjectService {

    User getCurrentUser();

    hasRole(String role);

    isPermitted(String permission);

    //more Subject 'wrapper' methods as necessary

    setSessionAttribute(Object key, Object value);
    removeSessionAttribute(key);

    //etc..
}

Then you would write a ShiroSubjectService implementation of this interface that calls the Shiro SecurityUtils/Subject API.

The benefit here is that all of your code deals with the SubjectService for everything, and never needs to 'know about', i.e. import any of Shiro's APIs - very loosely coupled.  Granted, you wouldn't need to use such an abstraction for wicket-shiro integration, but this approach is very useful in enterprise applications where you want to decouple you and your programming team from any 3rd party APIs as best as possible.

I hope that helps!

Cheers,

Les

On Sat, Jun 27, 2009 at 12:52 PM, Tauren Mills wrote:
Les,

Just wanted to let you know I renamed the project to wicket-shiro.  I also added a spring/hibernate example to it.  But I was wondering if I should be getting the username out of the subject in a better way than this:

@SpringBean(name = "userService")
private UserService userService;

public UserAuthHeader(String id, Class<? extends Page> loginPage)
{
  super( id );

  add( new Label( "name", new AbstractReadOnlyModel<String>() {
    @Override
    public String getObject() {
      Long id = (Long) SecurityUtils.getSubject().getPrincipal();
      return userService.getUser(id).getUsername();
    }
  }) );
}

With the user being a Hibernate User entity, the principle is a Long id.  So I'm using that to find the User from the userService and return the username. But is the User object stored in the Subject somewhere? I didn't locate it when I was tracing the code.

See here for the full java file:
https://wicket-stuff.svn.sourceforge.net/svnroot/wicket-stuff/trunk/wicketstuff-core/shiro-security/wicket-shiro-examples/shiro-example-spring-hibernate/src/main/java/org/wicketstuff/shiro/example/sprhib/UserAuthHeader.java

Thanks!
Tauren

Reply | Threaded
Open this post in threaded view
|

Re: wicket-ki-security

Tauren Mills-2
Les,

Thanks for posting this to the user list and your suggestions. The wicket-shiro project already had UserService implemented the way you suggested, but I just wasn't using it effectively in UserAuthHeader.  I've updated the UserAuthHeader to call getCurrentUser().  I have not added a ShiroService to the wicket-shiro sample, but I can see how it would prove useful in a real project. Perhaps I'll add it to the sample later.

So I'm curious about the status of getting Shiro into a maven repository.  Any estimates on when that might happen?

Thanks!
Tauren


On Sat, Jun 27, 2009 at 12:38 PM, Les Hazlewood <[hidden email]> wrote:
Hi Tauren,

I'm forwarding this to the Shiro user list as the answer could benefit other people as well.

The User object itself should probably never be stored in the Subject or its Session - just the ID as you've pointed out.  This is definitely the appropriate way to use Shiro for most enterprise applications.

The reason is that, especially with JPA/Hibernate applications, If Shiro stored the User object directly, it would bypass lots of optimizations that are in place in persistence frameworks, such as caching.

That is, you should almost always lookup the User object during a transaction via the Subject-accessible user ID, and rely on a nice caching framework to improve performance if you don't want an expensive 'round trip' to the database each time you do the lookup. 

This is especially important for coherency - if you cache a User object in the Subject (or the Subject's Session), and you change that User object somewhere else in the application (say, via an 'update account' web page), well, now the User object in your session is 'stale' and does not reflect the one changed by visiting the 'update account' web page.

This beneficial technique is not unique to just Shiro of course - most session values should usually be extremely lightweight objects - ids or lookup keys to pull the corresponding 'real' object via a Service or DAO call, relying on the underlying persistence framework and caching to help with performance, data versioning, coherency, etc.

So a good practice is to have a 'utility' helper component that can do this translation automatically.

For example, UserService#getCurrentUser():

public User getCurrentUser() {

    Long id = (Long)SecurityUtils.getSubject().getPrincipal();
    if ( id != null ) {
        // they are either authenticated or remembered from a previous session,
        // so return the user:

        return getUser(id);
    } else {
        //not logged in or remembered:
        return null;
    }
}

I've taken this concept a little further and I wrap all my SecurityUtils.getSubject().* method operations by creating a SubjectService interface:

public interface SubjectService {

    User getCurrentUser();

    hasRole(String role);

    isPermitted(String permission);

    //more Subject 'wrapper' methods as necessary

    setSessionAttribute(Object key, Object value);
    removeSessionAttribute(key);

    //etc..
}

Then you would write a ShiroSubjectService implementation of this interface that calls the Shiro SecurityUtils/Subject API.

The benefit here is that all of your code deals with the SubjectService for everything, and never needs to 'know about', i.e. import any of Shiro's APIs - very loosely coupled.  Granted, you wouldn't need to use such an abstraction for wicket-shiro integration, but this approach is very useful in enterprise applications where you want to decouple you and your programming team from any 3rd party APIs as best as possible.

I hope that helps!

Cheers,

Les


On Sat, Jun 27, 2009 at 12:52 PM, Tauren Mills wrote:
Les,

Just wanted to let you know I renamed the project to wicket-shiro.  I also added a spring/hibernate example to it.  But I was wondering if I should be getting the username out of the subject in a better way than this:

@SpringBean(name = "userService")
private UserService userService;

public UserAuthHeader(String id, Class<? extends Page> loginPage)
{
  super( id );

  add( new Label( "name", new AbstractReadOnlyModel<String>() {
    @Override
    public String getObject() {
      Long id = (Long) SecurityUtils.getSubject().getPrincipal();
      return userService.getUser(id).getUsername();
    }
  }) );
}

With the user being a Hibernate User entity, the principle is a Long id.  So I'm using that to find the User from the userService and return the username. But is the User object stored in the Subject somewhere? I didn't locate it when I was tracing the code.

See here for the full java file:
https://wicket-stuff.svn.sourceforge.net/svnroot/wicket-stuff/trunk/wicketstuff-core/shiro-security/wicket-shiro-examples/shiro-example-spring-hibernate/src/main/java/org/wicketstuff/shiro/example/sprhib/UserAuthHeader.java

Thanks!
Tauren