Hi Tauren,
The easiest way is to probably use the MethodInvokingFactoryBean for this:
<bean id="myRememberMeManager" class="...">
<property name="cipherKey" ref="cipherKeyBytes"/>
</bean>
<bean id="cipherKeyBytes"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.apache.shiro.codec.Base64"/>
<property name="targetMethod" value="decode"/>
<property name="arguments">
<list>
<value>your_base64_encoded_string</value>
</list>
</property>
</bean>
Cheers,
Les
On Wed, May 12, 2010 at 12:58 PM, Tauren Mills <[email protected]> wrote:
> Les,
> Thanks, this is helpful. I'd like to use my own custom cipherKey, but I'm
> not using INI configuration. Could you perhaps explain how to configure this
> using Spring instead of INI? I think this too would be useful information to
> go into the wiki. Here's my current spring config:
> <bean id="securityManager"
> class="org.apache.shiro.web.DefaultWebSecurityManager">
> <property name="realm" ref="myRealm"/>
> <property name="rememberMeManager" ref="myRememberMeManager"/>
> </bean>
> <bean name="myRememberMeManager"
> class="com.project.security.MyRememberMeManager"/>
> Thanks!
> Tauren
>
> On Wed, May 12, 2010 at 12:24 PM, Les Hazlewood <[email protected]>
> wrote:
>>
>> Yep, you're exactly right - it is because of the new (more secure)
>> CipherService instances. I would ignore these warnings - the
>> rememberMe cookie will be deleted and a new correct one will be
>> auto-created the next time they log in (and they click 'remember me'
>> of course).
>>
>> If you didn't want to ignore them and have them just work, you can do 3
>> things:
>>
>> 1. Configure the RememberMeManager to use the BlowfishCipherService.
>> Prior to the 1.0 release, we defaulted to using the Blowfish
>> algorithm, but most people prefer AES, so now we default to the
>> AesCipherService.
>> 2. Ensure that the new BlowfishCipherService is using the _exact_
>> same key that was used before. This is simple if you're using your
>> own cipher key. It is harder if you were using Shiro's default
>> blowfish key, at which point you would need to look at the old source
>> code to find it.
>> 3. Ensure that your BlowfishCipherService instance's
>> 'generateInitializationVectors' attribute is set to false.
>>
>> Step 3 makes the encryption less secure and overall 1-3 can be a pain.
>> I'd recommend just ignoring the warnings for now and let the new
>> cookies replace the old ones if you are ok with that.
>>
>> One thing you might want to try is using your own cipher key with the
>> CipherService instance - it makes it more secure instead of using
>> Shiro's default (which could be discovered by a 3rd party). To be
>> fair, the encrypted data is just a serialized PrincipalCollection, so
>> if you're not storing sensitive data in the PC, you don't really need
>> to do this. I still like to do it though in case I ever do need to
>> use an AES Cipher to encrypt something sensitive - then my key is all
>> ready to go.
>>
>> You can do this by doing the following:
>>
>> //AES algorithm only supports key sizes of 128, 192, and 256 bits.
>> //192 and 256 require that the JCE Unlimited Strength
>> //Jurisdiction Policy files to be installed.
>> int keySize = 128;
>> String key = Base64.encode(new
>> AesCipherService().generateNewKey(keySize).getEncoded());
>>
>> //then you can use this key in your shiro config:
>>
>> [main]
>> securityManager.rememberMeManager.cipherKey =
>> your_base64_encoded_string_from_above
>>
>> The configurator will automatically recognize that is a byte array
>> property and Base64 decode the string value into the correct bytes.
>> If you wanted to use Hex encoding instead, make sure you prefix your
>> encoded string with 0x ('zero' 'x'):
>>
>> securityManager.rememberMeManager.cipherKey = 0x123456789ABCDEF
>>
>> which will trigger Hex decoding instead.
>>
>> I know I've rambled on about this, but hopefully this will be indexed
>> by Google and help someone else in the future. It also gives me a
>> base to use to update the wiki documentation :)
>>
>> Cheers,
>>
>> Les
>>
>> 1. Ensure that the cipher service is using the same cipher key
>> you could configure your CipherService to set
>> 'generateInitializationVectors' to false, in which case the previou
>>
>> On Wed, May 12, 2010 at 11:08 AM, Tauren Mills <[email protected]> wrote:
>> > Also, I'm getting the following exception in my logs. I'm assuming that
>> > is
>> > just because people have old cookies with old cyphers, and that once
>> > their
>> > cookies are replaced, I won't see these exceptions anymore. I also
>> > assume
>> > this means that everyone is being forced to log in again since my app
>> > has
>> > been updated with the latest shiro and that no existing rememberme
>> > cookies
>> > will work. Is this correct?
>> > Thanks,
>> > Tauren
>> > ---
>> >
>> > WARN - DefaultSecurityManager - Delegate RememberMeManager instance
>> > of
>> > type [com.project.security.MyRememberMeManager] threw an exception
>> > during
>> > getRememberedPrincipals().
>> > org.apache.shiro.crypto.CryptoException: Unable to execute 'doFinal'
>> > with
>> > cipher instance [javax.crypto.cip...@5b3bd1c0].
>> > at
>> > org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:465)
>> > at
>> > org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:448)
>> > at
>> >
>> > org.apache.shiro.crypto.JcaCipherService.decrypt(JcaCipherService.java:393)
>> > at
>> >
>> > org.apache.shiro.crypto.JcaCipherService.decrypt(JcaCipherService.java:385)
>> > at
>> >
>> > org.apache.shiro.mgt.AbstractRememberMeManager.decrypt(AbstractRememberMeManager.java:491)
>> > at
>> >
>> > org.apache.shiro.mgt.AbstractRememberMeManager.convertBytesToPrincipals(AbstractRememberMeManager.java:431)
>> > at
>> >
>> > org.apache.shiro.mgt.AbstractRememberMeManager.getRememberedPrincipals(AbstractRememberMeManager.java:398)
>> > at
>> >
>> > com.project.security.MyRememberMeManager.getRememberedPrincipals(MyRememberMeManager.java:21)
>> > at
>> >
>> > org.apache.shiro.mgt.DefaultSecurityManager.getRememberedIdentity(DefaultSecurityManager.java:526)
>> > at
>> >
>> > org.apache.shiro.mgt.DefaultSecurityManager.resolvePrincipals(DefaultSecurityManager.java:403)
>> > at
>> >
>> > org.apache.shiro.mgt.DefaultSecurityManager.createSubject(DefaultSecurityManager.java:320)
>> > at
>> > org.apache.shiro.subject.Subject$Builder.buildSubject(Subject.java:767)
>> > at
>> >
>> > org.apache.shiro.web.subject.WebSubject$Builder.buildWebSubject(WebSubject.java:101)
>> > at org.apache.shiro.web.servlet.ShiroFilter.bind(ShiroFilter.java:540)
>> > at
>> >
>> > org.apache.shiro.web.servlet.ShiroFilter.doFilterInternal(ShiroFilter.java:627)
>> > at
>> >
>> > org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:83)
>> > at
>> >
>> > org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1088)
>> > at
>> >
>> > org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
>> > at
>> >
>> > org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
>> > at
>> >
>> > org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1088)
>> > at
>> > org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
>> > at
>> >
>> > org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
>> > at
>> > org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
>> > at
>> > org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
>> > at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
>> > at
>> > org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
>> > at org.mortbay.jetty.Server.handle(Server.java:324)
>> > at
>> > org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
>> > at
>> >
>> > org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:829)
>> > at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:513)
>> > at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
>> > at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
>> > at
>> >
>> > org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
>> > at
>> >
>> > org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)
>> > Caused by: javax.crypto.BadPaddingException: Given final block not
>> > properly
>> > padded
>> > at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
>> > at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
>> > at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
>> > at javax.crypto.Cipher.doFinal(DashoA13*..)
>> > at
>> > org.apache.shiro.crypto.JcaCipherService.crypt(JcaCipherService.java:462)
>> > ... 33 more
>> >
>> > On Wed, May 12, 2010 at 9:49 AM, Tauren Mills <[email protected]> wrote:
>> >>
>> >> Les,
>> >> Thanks for the help. However, now I'm having a problem
>> >> with getRememberedPrincipals() being called dozens of times per page
>> >> view, causing my "accessed" property to be updated many times, which is
>> >> obviously not ideal.
>> >> I believe this is because my application is running in Jetty, so every
>> >> image, JS, CSS, and other resource is causing a request to be created.
>> >> My
>> >> cookie is set to / since I need it to work for many different paths.
>> >> Any suggestions on how to only update my "accessed" property a single
>> >> time
>> >> per session? I don't need the property updated with every request,
>> >> just
>> >> when they first access the site for a session is fine. Basically, the
>> >> property I currently called "accessed" would actually be more
>> >> accurately
>> >> called "lastLogin".
>> >> Below is code if it helps.
>> >> Thanks,
>> >> Tauren
>> >> ---
>> >> public class MyRememberMeManager extends CookieRememberMeManager {
>> >> private MemberService memberService;
>> >> �...@autowired
>> >> public void setMemberService(MemberService memberService) {
>> >> this.memberService = memberService;
>> >> }
>> >> @Override
>> >> public PrincipalCollection getRememberedPrincipals(SubjectContext
>> >> subjectContext) {
>> >> PrincipalCollection principals =
>> >> super.getRememberedPrincipals(subjectContext);
>> >> if ( principals != null ) {
>> >> Long id = (Long) principals.getPrimaryPrincipal();
>> >> memberService.updateAccessed(id);
>> >> }
>> >> return principals;
>> >> }
>> >> }
>> >> // MemberServiceImpl.java
>> >> public void updateAccessed(Long id) {
>> >> updateAccessed(findById(id));
>> >> }
>> >> public void updateAccessed(Member member) {
>> >> member.setAccessed(new Date());
>> >> memberDao.save(member);
>> >> }
>> >>
>> >> // Spring config
>> >> <bean id="securityManager"
>> >> class="org.apache.shiro.web.DefaultWebSecurityManager">
>> >> property instead. -->
>> >> <property name="realm" ref="myRealm"/>
>> >> <property name="rememberMeManager" ref="myRememberMeManager"/>
>> >> </bean>
>> >> <bean name="myRememberMeManager"
>> >> class="com.project.security.MyRememberMeManager"/>
>> >>
>> >> On Tue, May 11, 2010 at 10:11 AM, Les Hazlewood <[email protected]>
>> >> wrote:
>> >>>
>> >>> Hi Tauren,
>> >>>
>> >>> Yes, WebRememberMeManager has been deprecated in favor of the new
>> >>> CookieRememberMeManager which uses a new Cookie property and not the
>> >>> (now deprecated) CookieAttribute concept. CookieAttribute was complex
>> >>> and confusing. The new Cookie interface and implementation in
>> >>> comparison are much easier to understand and configure. So, if you
>> >>> extend CookieRememberMeManager, you should be closely back to where
>> >>> you were when you extended WebRememberMeManager.
>> >>>
>> >>> As for the SubjectContext - it was added within the last two weeks to
>> >>> satisfy a technical requirement: the (now new) SubjectContext.resolve*
>> >>> method behavior was needed in multiple places across more than one OO
>> >>> hierarchy. So, we either had to copy-n-paste logic (yuck) or
>> >>> consolidate it into a component. The SubjectContext satisfies this
>> >>> need and will stay in place instead of the raw Map.
>> >>>
>> >>> Also note that for 1.0, we'll be removing all Deprecated classes and
>> >>> methods. Since we're wrapping up coding issues today (and possibly
>> >>> tomorrow), we'll be deleting them any time now.
>> >>>
>> >>> I hope that helps!
>> >>>
>> >>> Les
>> >>>
>> >>> On Tue, May 11, 2010 at 9:22 AM, Tauren Mills <[email protected]>
>> >>> wrote:
>> >>> > I'm just getting back to this issue now, and have updated to the
>> >>> > latest
>> >>> > shiro snapshot.
>> >>> > When I started working on this, I had extended WebRememberMeManager,
>> >>> > but it
>> >>> > looks like it is now depricated. What is the recommended approach at
>> >>> > this
>> >>> > time? Should I be directly extending AbstractRememberMeManager?
>> >>> > I also wanted to point out that your example in this thread shows
>> >>> > passing a
>> >>> > Map to getRememberedPrincipals, but in my experience I had to pass a
>> >>> > SubjectContext. Before I proceed further, I just want to make sure
>> >>> > that I'm
>> >>> > heading down the proper path that will be supported in the future,
>> >>> > and
>> >>> > not
>> >>> > implement something that I'll have to change later.
>> >>> > Thanks!
>> >>> > Tauren
>> >>> >
>> >>> >
>> >>> >
>> >>> >
>> >>> >
>> >>> >
>> >>> > On Mon, Apr 19, 2010 at 12:14 PM, Les Hazlewood
>> >>> > <[email protected]>
>> >>> > wrote:
>> >>> >>
>> >>> >> I committed a change to how cryptography works on Friday - that
>> >>> >> might
>> >>> >> have done something with it (we encrypt principals by default).
>> >>> >> However, all the test cases still passed and I know that we do have
>> >>> >> some test cases around remember me cookies
>> >>> >> (WebRememberMeManagerTest).
>> >>> >>
>> >>> >> Please open a Jira issue in the mean time so we don't lose it.
>> >>> >> I'll
>> >>> >> try to run the sample web app(s) to see if they exhibit the same
>> >>> >> behavior.
>> >>> >>
>> >>> >> Thanks!
>> >>> >>
>> >>> >> Les
>> >>> >>
>> >>> >> On Mon, Apr 19, 2010 at 10:57 AM, Tauren Mills <[email protected]>
>> >>> >> wrote:
>> >>> >> > It seems that my remember me function got broken at some point.
>> >>> >> > All
>> >>> >> > of
>> >>> >> > my
>> >>> >> > requests do not have the cookie they used to. I've tried logging
>> >>> >> > off
>> >>> >> > and
>> >>> >> > back in again, but still no cookie. This is
>> >>> >> > causing getRememberedPrincipals
>> >>> >> > to run with every single request. Obviously not good.
>> >>> >> > Any suggestions on how to track down where the remember me
>> >>> >> > problem
>> >>> >> > is
>> >>> >> > coming
>> >>> >> > from - why I'm not getting cookies? I haven't done much of
>> >>> >> > anything
>> >>> >> > with
>> >>> >> > Shiro for months now, and it used to work, so not sure what
>> >>> >> > happened.
>> >>> >> > Was it
>> >>> >> > broken in some recent release that I may be using?
>> >>> >> > Thanks,
>> >>> >> > Tauren
>> >>> >> > On Mon, Apr 19, 2010 at 3:41 AM, Tauren Mills
>> >>> >> > <[email protected]>
>> >>> >> > wrote:
>> >>> >> >>
>> >>> >> >> Les,
>> >>> >> >> Thanks, I'll look into doing this. Are there any examples of
>> >>> >> >> using
>> >>> >> >> an
>> >>> >> >> AuthenticationListener that I could reference?
>> >>> >> >>
>> >>> >> >> Tauren
>> >>> >> >>
>> >>> >> >> On Fri, Apr 16, 2010 at 9:10 AM, Les Hazlewood
>> >>> >> >> <[email protected]>
>> >>> >> >> wrote:
>> >>> >> >>>
>> >>> >> >>> Hi Tauren,
>> >>> >> >>>
>> >>> >> >>> You could implement an AuthenticationListener - I typically
>> >>> >> >>> like
>> >>> >> >>> to do
>> >>> >> >>> this for these kinds of operations.
>> >>> >> >>>
>> >>> >> >>> Since Shiro does not count remember me as a true
>> >>> >> >>> authentication,
>> >>> >> >>> the
>> >>> >> >>> AuthenticationListener will not be triggered for remember me
>> >>> >> >>> events.
>> >>> >> >>> To do that you would also need to implement your own
>> >>> >> >>> RememberMeManager. Typically subclassing the default one and
>> >>> >> >>> updating
>> >>> >> >>> the timestamp in the getRememberedPrincipals method would work:
>> >>> >> >>>
>> >>> >> >>> @Override
>> >>> >> >>> public PrincipalCollection getRememberedPrincipals(Map
>> >>> >> >>> subjectContext)
>> >>> >> >>> {
>> >>> >> >>> PrincipalCollection principals =
>> >>> >> >>> super.getRememberedPrincipals(subjectContext);
>> >>> >> >>> if ( principals != null ) {
>> >>> >> >>> int userId = principals.getPrimaryPrincipal();
>> >>> >> >>> //update the last login timestamp for user with this id
>> >>> >> >>> here
>> >>> >> >>> }
>> >>> >> >>> }
>> >>> >> >>>
>> >>> >> >>> The above method is called only if the current Subject/session
>> >>> >> >>> does
>> >>> >> >>> not yet have an identity associated with it.
>> >>> >> >>>
>> >>> >> >>> The AuthenticationListener + the above overridden method should
>> >>> >> >>> do
>> >>> >> >>> it!
>> >>> >> >>>
>> >>> >> >>> Cheers,
>> >>> >> >>>
>> >>> >> >>> Les
>> >>> >> >>>
>> >>> >> >>> On Thu, Apr 15, 2010 at 8:37 PM, Tauren Mills
>> >>> >> >>> <[email protected]>
>> >>> >> >>> wrote:
>> >>> >> >>> > What would be the best way to update properties of a member
>> >>> >> >>> > on
>> >>> >> >>> > successful
>> >>> >> >>> > authentication? Basically, there is a Member.lastLogin
>> >>> >> >>> > property
>> >>> >> >>> > that
>> >>> >> >>> > I
>> >>> >> >>> > want
>> >>> >> >>> > updated with the current date every time a member comes back
>> >>> >> >>> > to
>> >>> >> >>> > a
>> >>> >> >>> > site
>> >>> >> >>> > and
>> >>> >> >>> > logs in. However, this should also support rememberme
>> >>> >> >>> > authentications. I
>> >>> >> >>> > don't necessarily want to update the lastLogin property on
>> >>> >> >>> > every
>> >>> >> >>> > single
>> >>> >> >>> > interaction the user has on the site, just once for each
>> >>> >> >>> > session.
>> >>> >> >>> > Is there some technique in Shiro to do this, a callback of
>> >>> >> >>> > some
>> >>> >> >>> > sort,
>> >>> >> >>> > or
>> >>> >> >>> > some other method? Or should I be doing this in my
>> >>> >> >>> > application?
>> >>> >> >>> > Also, I can't seem to view much of the documentation. All the
>> >>> >> >>> > links
>> >>> >> >>> > on
>> >>> >> >>> > the
>> >>> >> >>> > following page require an Apache login. Is it supposed to be
>> >>> >> >>> > this
>> >>> >> >>> > way?
>> >>> >> >>> > http://incubator.apache.org/shiro/core.html
>> >>> >> >>> > Thanks!
>> >>> >> >>> > Tauren
>> >>> >> >>> >
>> >>> >> >>> >
>> >>> >> >>
>> >>> >> >
>> >>> >> >
>> >>> >
>> >>> >
>> >>
>> >
>> >
>
>