Les, Thanks, this works perfectly. Note that I had to use encodeToString() instead of encode():
int keySize = 128; String key = Base64.encodeToString(new AesCipherService().generateNewKey(keySize).getEncoded()); I'll just ignore the exceptions in the logs, as they will eventually go away when everyone has a new cookie. Tauren On Wed, May 12, 2010 at 1:51 PM, Les Hazlewood <[email protected]>wrote: > 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 > >> >>> >> >>> > > >> >>> >> >>> > > >> >>> >> >> > >> >>> >> > > >> >>> >> > > >> >>> > > >> >>> > > >> >> > >> > > >> > > > > > >
