Shiro缓存的使用、密码加密、Kaptcha验证码
一.缓存的使用
在shiro使用缓存有三种方式:自定义的缓存(如redis),EhCacheManager,MemoryConstrainedCacheManager
自定义缓存需要自己编写类去实现CacheManager和Cache接口,后二者shiro则已经实现
它们需要在配置文件里添加对应的bean,在然后在securityManager注册即可使用
1 /** 2 *这个缓存要XML配置文件,比较重 3 */ 4 @Bean 5 public EhCacheManager ehCacheManager() { 6 EhCacheManager cacheManager = new EhCacheManager(); 7 // cacheManager.setCacheManagerConfigFile(""); 8 return cacheManager; 9 } 10 11 /** 12 *shiro自带的 提供内存级的缓存 13 */ 14 @Bean 15 public MemoryConstrainedCacheManager memoryConstrainedCacheManager(){ 16 MemoryConstrainedCacheManager memoryConstrainedCacheManager=new MemoryConstrainedCacheManager(); 17 return memoryConstrainedCacheManager; 18 } 19 20 @Bean 21 public SecurityManager securityManager(UserRealm userRealm) { 22 logger.info("注入Shiro的Web过滤器-->securityManager", ShiroFilterFactoryBean.class); 23 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 24 25 securityManager.setRealm(userRealm); 26 //注入缓存管理器; 27 //更新权限信息后,记得在service里清除缓存 28 securityManager.setCacheManager(memoryConstrainedCacheManager());//这个如果执行多次,也是同样的一个对象; 29 return securityManager; 30 }
然后我们要在Realm类里提供清除缓存的方法
缓存在用户logout和非正常logout后都会清除,但是login状态下修改了权限就要我们在service里手动清除缓存
1 /** 2 * 清除缓存 3 */ 4 public void clearCached(){ 5 PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals(); 6 super.clearCache(principals); 7 }
二.密码加密
在配置文件里配置一个bean,用于给输入的密码使用MD5方式加密,加密1024次:
1 /** 2 * MD5加密配置 3 */ 4 @Bean 5 public HashedCredentialsMatcher hashedCredentialsMatcher(){ 6 HashedCredentialsMatcher hashedCredentialsMatcher=new HashedCredentialsMatcher(); 7 hashedCredentialsMatcher.setHashAlgorithmName("MD5"); 8 //加密次数 9 hashedCredentialsMatcher.setHashIterations(1024); 10 return hashedCredentialsMatcher; 11 }
在 securityManager里设置Realm的加密方法:
1 @Bean 2 public SecurityManager securityManager(UserRealm userRealm) { 3 logger.info("注入Shiro的Web过滤器-->securityManager", ShiroFilterFactoryBean.class); 4 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 5 //设置密码加密方法 6 userRealm.setCredentialsMatcher(hashedCredentialsMatcher()); 7 securityManager.setRealm(userRealm); 8 //注入缓存管理器; 9 //更新权限信息后,记得在service里清除缓存 10 securityManager.setCacheManager(memoryConstrainedCacheManager());//这个如果执行多次,也是同样的一个对象; 11 return securityManager; 12 }
用户登录时会把账号密码装在Token里传入doGetAuthenticationInfo()方法验证,我们改一下其中的校验
1 /** 2 * 登录认证 3 */ 4 @Override 5 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 6 //UsernamePasswordToken对象用来存放提交的登录信息 7 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; 8 //查出是否有此用户 9 User user = userService.findUserByName(token.getUsername()); 10 if (user != null) { 11 //盐 一般是用户名 12 ByteSource credentialsSalt = ByteSource.Util.bytes(user.getNickname()); 13 // 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验 14 return new SimpleAuthenticationInfo(user.getNickname(), user.getPswd(),credentialsSalt ,getName()); 15 } 16 return null; 17 }
主要是在校验中加了盐,这个盐在这我使用的是用户名,可以改。使用盐的目的是使MD5加密的结果不容易逆向破解。
最后我们把数据库里的明文密码改成加密后的:
1 public void passwordGen(){ 2 String hashAlgorithmName = "MD5"; 3 String credentials = "123456"; 4 int hashIterations = 1024; 5 ByteSource credentialsSalt = ByteSource.Util.bytes("admin"); 6 Object obj = new SimpleHash(hashAlgorithmName, credentials, credentialsSalt, hashIterations); 7 System.out.println(obj); 8 }
三 .Kaptcha验证码
maven引包
1 <!-- 验证码 --> 2 <dependency> 3 <groupId>com.github.penggle</groupId> 4 <artifactId>kaptcha</artifactId> 5 <version>2.3.2</version> 6 </dependency>
新建配置类
1 package com.tqh.demo.config; 2 3 import com.google.code.kaptcha.impl.DefaultKaptcha; 4 import org.springframework.context.annotation.Bean; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.stereotype.Component; 7 8 import java.util.Properties; 9 import com.google.code.kaptcha.util.Config; 10 /** 11 * @Author: Mcorleon 12 * @Date: 18-7-31 17:04 13 */ 14 @Configuration 15 public class KaptchaConfig { 16 @Bean 17 public DefaultKaptcha getDefaultKaptcha(){ 18 com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha(); 19 Properties properties = new Properties(); 20 properties.setProperty("kaptcha.border", "no"); 21 properties.setProperty("kaptcha.border.color", "105,179,90"); 22 properties.setProperty("kaptcha.textproducer.font.color", "red"); 23 properties.setProperty("kaptcha.image.width", "100"); 24 properties.setProperty("kaptcha.image.height", "40"); 25 properties.setProperty("kaptcha.textproducer.font.size", "35"); 26 properties.setProperty("kaptcha.background.clear.from", "white"); 27 properties.setProperty("kaptcha.background.clear.to", "white"); 28 properties.setProperty("kaptcha.session.key", "code"); 29 properties.setProperty("kaptcha.textproducer.char.length", "4"); 30 properties.setProperty("kaptcha.textproducer.font.names", "微软雅黑"); 31 Config config = new Config(properties); 32 defaultKaptcha.setConfig(config); 33 34 return defaultKaptcha; 35 } 36 } 37 // kaptcha.border 图片边框,合法值:yes , no yes 38 // kaptcha.border.color 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. black 39 // kaptcha.border.thickness 边框厚度,合法值:>0 1 40 // kaptcha.image.width 图片宽 200 41 // kaptcha.image.height 图片高 50 42 // kaptcha.producer.impl 图片实现类 com.google.code.kaptcha.impl.DefaultKaptcha 43 // kaptcha.textproducer.impl 文本实现类 com.google.code.kaptcha.text.impl.DefaultTextCreator 44 // kaptcha.textproducer.char.string 文本集合,验证码值从此集合中获取 abcde2345678gfynmnpwx 45 // kaptcha.textproducer.char.length 验证码长度 5 46 // kaptcha.textproducer.font.names 字体 Arial, Courier 47 // kaptcha.textproducer.font.size 字体大小 40px. 48 // kaptcha.textproducer.font.color 字体颜色,合法值: r,g,b 或者 white,black,blue. black 49 // kaptcha.textproducer.char.space 文字间隔 2 50 // kaptcha.noise.impl 干扰实现类 com.google.code.kaptcha.impl.DefaultNoise 51 // kaptcha.noise.color 干扰颜色,合法值: r,g,b 或者 white,black,blue. black 52 // kaptcha.obscurificator.impl 图片样式: 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy com.google.code.kaptcha.impl.WaterRipple 53 // kaptcha.background.impl 背景实现类 com.google.code.kaptcha.impl.DefaultBackground 54 // kaptcha.background.clear.from 背景颜色渐变,开始颜色 light grey 55 // kaptcha.background.clear.to 背景颜色渐变,结束颜色 white 56 // kaptcha.word.impl 文字渲染器 com.google.code.kaptcha.text.impl.DefaultWordRenderer 57 // kaptcha.session.key session key KAPTCHA_SESSION_KEY 58 // kaptcha.session.date session date KAPTCHA_SESSION_DATE
控制器里新建一个用于产生验证码图片的URI
1 @Autowired 2 private DefaultKaptcha defaultKaptcha; 3 4 @RequestMapping ("/defaultKaptcha") 5 public void defaultKaptcha(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception { 6 byte[] captchaChallengeAsJpeg; 7 ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); 8 try { 9 //生产验证码字符串并保存到session中 10 String createText = defaultKaptcha.createText(); 11 httpServletRequest.getSession().setAttribute("vrifyCode", createText); 12 //使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中 13 BufferedImage challenge = defaultKaptcha.createImage(createText); 14 ImageIO.write(challenge, "jpg", jpegOutputStream); 15 } catch (IllegalArgumentException e) { 16 httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND); 17 return; 18 } 19 20 //定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组 21 captchaChallengeAsJpeg = jpegOutputStream.toByteArray(); 22 httpServletResponse.setHeader("Cache-Control", "no-store"); 23 httpServletResponse.setHeader("Pragma", "no-cache"); 24 httpServletResponse.setDateHeader("Expires", 0); 25 httpServletResponse.setContentType("image/jpeg"); 26 ServletOutputStream responseOutputStream = 27 httpServletResponse.getOutputStream(); 28 responseOutputStream.write(captchaChallengeAsJpeg); 29 responseOutputStream.flush(); 30 responseOutputStream.close(); 31 }
注意:要在shiro配置类中设置过滤链不拦截 /defaultKaptcha
验证码html: <input type="text" name="vrifyCode" /> <img alt="验证码" onclick = "this.src='/defaultKaptcha?d='+new Date()*1" src="/defaultKaptcha" />
点击可更换
在login的控制器中从session取验证码与输入的比对即可