Shiro 参考Shiro安全框架【快速入门】就这一篇! - 知乎 (zhihu.com)
做什么的
开源安全框架,处理身份验证、授权、加密和会话管理
认证:识别用户身份(登录)
授权:给用户某些操作的权限(赋予角色)
加密:对数据源使用加密算法
会话管理:特定于用户的会话管理
整体架构
分为Subject,SecurityManager和 Realm三层
核心组件
1 2 3 可以是一个通过浏览器请求的用户,也可能是一个运行的程序 Subject在shiro中是一个接口,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权
1 2 3 4 5 shiro的核心 对所有的subject进行安全管理,可以完成subject的认证、授权等 通过Authenticator认证器进行认证 通过Authorizer授权器进行授权 通过SessionManager进行会话管理
1 2 数据库读取+认证功能+授权功能实现 相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据
入门案例 身份认证 从ini文件读取
导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependency > <groupId > commons-logging</groupId > <artifactId > commons-logging</artifactId > <version > 1.1.3</version > </dependency > <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-core</artifactId > <version > 1.3.2</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.11</version > </dependency >
在resource目录下编写ini文件
编写测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class HelloShiro { @Test public void shiroLogin () { IniSecurityManagerFactory factory = new IniSecurityManagerFactory ("classpath:shiro.ini" ); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken ("jay" , "123" ); subject.login(token); System.out.println("是否登录成功:" + subject.isAuthenticated()); } }
从Realm中读取
编写service,模拟从数据库读取用户密码
1 2 3 4 5 6 7 public class SecurityServiceImpl implements SecurityService { @Override public String findPasswordByLoginName (String loginName) { return "123" ; } }
自定义Realm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class DefinitionRealm extends AuthorizingRealm { SecurityService securityService = new SecurityServiceImpl (); @Override protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) { return null ; } @Override protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException { String loginName = (String) authenticationToken.getPrincipal(); String password = securityService.findPasswordByLoginName(loginName); if (password == null || "" .equals(password)) throw new UnknownAccountException ("账户不存在" ); return new SimpleAuthenticationInfo (loginName, password, this .getName()); } }
在ini文件中设置realm
1 2 3 [main] definitionRealm =com.xw.realm.DefinitionRealmsecurityManager.realms =$definitionRealm
编写测试类
和上面的一样
认证过程
1 2 Subject -> SecurityManager -> Authenticator -> Realm -> 自定义Realm将数据库中查到的账号密码返回 最后在Realm中比较Subject传过来的Token和数据库中的是否一致
编码解码 通过shiro的Hex和Base64来进行编码解码
编写工具类(把两种方式整合到一个工具类上)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class EncodesUtils { public static String encodeHex (byte [] bytes) { return Hex.encodeToString(bytes); } public static byte [] decodeHex(String s) { return Hex.decode(s); } public static String encodeBase64 (byte [] bytes) { return Base64.encodeToString(bytes); } public static byte [] decodeBase64(String s) { return Base64.decode(s); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test1 () { String pwd = "123456" ; String encode = EncodesUtils.encodeHex(pwd.getBytes()); String decode = new String (EncodesUtils.decodeHex(encode)); System.out.println(decode.equals(pwd) ? "相等" : "不相等" ); encode = EncodesUtils.encodeBase64(pwd.getBytes()); decode = new String (EncodesUtils.decodeBase64(encode)); System.out.println(decode.equals(pwd) ? "相等" : "不相等" ); }
散列算法
通过SimpleHash(算法名称,原密码,盐值,加密次数)来进行加密
编写工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class DigestUtil { private static final String SHA1 = "SHA-1" ; private static final Integer ITERATIONS = 512 ; public static String sha1 (String s, String salt) { return new SimpleHash (SHA1, s, salt, ITERATIONS).toString(); } public static Map<String, String> entryptPassword (String passwordPlain) { HashMap<String, String> map = new HashMap <>(); String salt = generateSalt(); map.put("salt" , salt); map.put("password" , sha1(passwordPlain, salt)); return map; } public static String generateSalt () { SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator (); return randomNumberGenerator.nextBytes().toHex(); } }
测试
1 2 3 4 5 6 @Test public void test2 () { String pwd = "123456" ; Map<String, String> map = DigestUtil.entryptPassword(pwd); System.out.println(map.toString()); }
Realm中指定匹配器
自定义匹配器的盐值,加密次数
service层返回加密后的密码和盐值
1 2 3 4 5 6 7 public class SecurityServiceImpl implements SecurityService { @Override public Map<String, String> findPasswordByLoginName (String loginName) { return DigestUtil.entryptPassword("123" ); } }
自定义Realm重写构造函数,设置匹配器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class DefinitionRealm extends AuthorizingRealm { SecurityService securityService = new SecurityServiceImpl (); public DefinitionRealm () { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher (DigestUtil.SHA1); matcher.setHashIterations(DigestUtil.ITERATIONS); setCredentialsMatcher(matcher); } @Override protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) { return null ; } @Override protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException { String loginName = (String) authenticationToken.getPrincipal(); Map<String, String> map = securityService.findPasswordByLoginName(loginName); if (map.isEmpty()) throw new UnknownAccountException ("账户不存在" ); String password = map.get("password" ); String salt = map.get("salt" ); return new SimpleAuthenticationInfo (loginName, password, ByteSource.Util.bytes(salt), this .getName()); } }
测试
身份授权
service层返回查询到的角色、权限信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public List<String> findRoleByLoginName (String loginName) { List<String> list = new ArrayList <>(); list.add("admin" ); list.add("dev" ); return list; }@Override public List<String> findPermissionByLoginName (String loginName) { List<String> list = new ArrayList <>(); list.add("order:add" ); list.add("order:list" ); list.add("order:del" ); return list; }
Realm中构建校验信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class DefinitionRealm extends AuthorizingRealm { SecurityService securityService = new SecurityServiceImpl (); @Override protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) { String loginName = (String) principalCollection.getPrimaryPrincipal(); List<String> roles = securityService.findRoleByLoginName(loginName); List<String> permissions = securityService.findPermissionByLoginName(loginName); SimpleAuthorizationInfo authenticationInfo = new SimpleAuthorizationInfo (); authenticationInfo.addRoles(roles); authenticationInfo.addStringPermissions(permissions); return authenticationInfo; } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Test public void testPermissionRealm () { Subject subject = shiroLogin(); System.out.println("是否有admin角色:" + subject.hasRole("admin" )); System.out.println("是否有coder角色:" + subject.hasRole("coder" )); System.out.println("是否有添加订单权限:" + subject.isPermitted("order:add" )); System.out.println("是否有修改订单权限:" + subject.isPermitted("order:update" )); }public Subject shiroLogin () { IniSecurityManagerFactory factory = new IniSecurityManagerFactory ("classpath:shiro.ini" ); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken ("jay" , "123" ); subject.login(token); System.out.println("是否登录成功:" + subject.isAuthenticated()); return subject; }
使用案例 Shiro认证过程
验证用户身份
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class AuthenticationTest { SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm (); @Before public void addUser () { simpleAccountRealm.addAccount("user" , "123456" ); } @Test public void testAuthentication () { DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager (); defaultSecurityManager.setRealm(simpleAccountRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken ("user" , "123456" ); subject.login(token); System.out.println("isAuthenticated:" + subject.isAuthenticated()); subject.logout(); System.out.println("isAuthenticated:" + subject.isAuthenticated()); } }
Shiro授权过程
赋予/验证用户角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class AuthenticationTest2 { SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm (); @Before public void addUser () { simpleAccountRealm.addAccount("user" , "123456" , "admin" , "user" ); } @Test public void testAuthentication () { DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager (); defaultSecurityManager.setRealm(simpleAccountRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken ("user" , "123456" ); subject.login(token); System.out.println("isAuthenticated:" + subject.isAuthenticated()); subject.checkRoles("admin" , "user" ); } }
自定义Realm
realm类似MVC的repository层
自定义Realm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 public class MyRealm extends AuthorizingRealm { Map<String, String> userMap = new HashMap <>(16 ); { userMap.put("user" , "123456" ); super .setName("myRealm" ); } @Override protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) { String userName = (String) principalCollection.getPrimaryPrincipal(); Set<String> roles = getRolesByUserName(userName); Set<String> permissions = getPermissionsByUserName(userName); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo (); simpleAuthorizationInfo.setStringPermissions(permissions); simpleAuthorizationInfo.setRoles(roles); return simpleAuthorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException { String username = (String) authenticationToken.getPrincipal(); String password = getPasswordByUserName(username); if (password == null ) { return null ; } SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo ("user" , password, "myRealm" ); return simpleAuthenticationInfo; } private Set<String> getPermissionsByUserName (String username) { HashSet<String> permission = new HashSet <>(); permission.add("user:delete" ); permission.add("user:add" ); return permission; } private Set<String> getRolesByUserName (String userName) { Set<String> roles = new HashSet <>(); roles.add("admin" ); roles.add("user" ); return roles; } private String getPasswordByUserName (String userName) { return userMap.get(userName); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class AuthenticationTest3 { MyRealm myRealm = new MyRealm (); @Test public void testAuthentication () { DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager (); defaultSecurityManager.setRealm(myRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken ("user" , "123456" ); subject.login(token); subject.checkRoles("admin" , "user" ); subject.checkPermission("user:add" ); } }
Shiro加密
使用MD5加密可以使数据库中的密码不是明文保存,数据库泄露的损失会减小
但可以通过用一些简单常用的密码来撞库,从而反推原密码
解决方式:
加盐: 在原始密码上加上随机数,再进行MD5加密。需要把随机数也存到数据库中,以便之后进行验证
多次加密 :多次加密MD5,从而让攻击者不知道加密的次数
Shiro实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class AuthenticationTest4 { @Test public void testAuthentication () { String password = "123456" ; String salt = new SecureRandomNumberGenerator ().nextBytes().toString(); int times = 2 ; String alogrithmName = "md5" ; SimpleHash encodePassword = new SimpleHash (alogrithmName, password, salt, times); System.out.println("原密码:" + password); System.out.println("加密后:" + encodePassword); } }
小dmeo
SpringBoot集成Shiro SpringBoot之整合Shiro springboot+shiro
整合Shiro 导入依赖 1 2 3 4 5 6 <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-spring-boot-starter</artifactId > <version > 1.5.3</version > </dependency >
编写自定义Realm 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.xw.shiro.realm;public class CustomerRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) { return null ; } @Override protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException { return null ; } }
编写Shiro配置类 主要配置过滤器、SecurityManager、Realm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.xw.config;import java.util.HashMap;import java.util.Map;@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean (DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean (); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> map = new HashMap <String, String>(); map.put("/index.jsp" ,"authc" ); shiroFilterFactoryBean.setLoginUrl("/login.jsp" ); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager (Realm realm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager (); securityManager.setRealm(realm); return securityManager; } @Bean public Realm getRealm () { CustomerRealm customerRealm = new CustomerRealm (); return customerRealm; } }
登录登出 编写Controller 登录登出方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.xw.controller;@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/logout") public String logout () { Subject subject = SecurityUtils.getSubject(); subject.logout(); return "redirect:/login.jsp" ; } @RequestMapping("/login") public String login (String username, String password) { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken (username, password); try { subject.login(token); System.out.println("登录成功" ); return "redirect:/index.jsp" ; } catch (UnknownAccountException e) { e.printStackTrace(); System.out.println("用户错误" ); } catch (IncorrectCredentialsException e) { System.out.println("密码错误" ); } return "redirect:/login.jsp" ; } }
编写自定义Realm的认证方法 1 2 3 4 5 6 7 8 9 10 11 12 @Override protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException { String username = (String) authenticationToken.getPrincipal(); String account = "zhangsan" ; String password = "123456" ; if (account.equals(username)) { return new SimpleAuthenticationInfo (username, password, this .getName()); } return null ; }
修改配置类 将login方法设为公共资源,其他的为受限资源
1 2 3 Map<String, String> map = new HashMap <String, String>(); map.put("/user/login" ,"anon" ); map.put("/**" ,"authc" );
MD5、Salt认证 编写生成盐值工具类 1 2 3 4 5 6 7 package com.xw.utils;public class SaltUtil { public static String getSalt () { return new SecureRandomNumberGenerator ().nextBytes().toString(); } }
编写获取对象工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.xw.utils;@Component public class ApplicationContextUtil implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { this .context = applicationContext; } public static Object getBean (String beanName) { return context.getBean(beanName); } }
编写Service注册方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.xw.service.impl;@Service("userService") public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public void register (User user) { String salt = SaltUtil.getSalt(); user.setSalt(salt); Md5Hash MD5 = new Md5Hash (user.getPassword(), salt, 1024 ); user.setPassword(MD5.toHex()); userMapper.save(user); } @Override public User findByUsername (String username) { return userMapper.findByUsername(username); } }
自定义Realm中改为从Service中获取数据 1 2 3 4 5 6 7 8 9 10 11 12 @Override protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException { String username = (String) authenticationToken.getPrincipal(); UserService userService = (UserService) ApplicationContextUtil.getBean("userService" ); User user = userService.findByUsername(username); if (!ObjectUtils.isEmpty(user)) { return new SimpleAuthenticationInfo (user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()), this .getName()); } return null ; }
修改配置类 修改公共资源和添加匹配器
1 2 3 4 map.put("/user/login" ,"anon" ); map.put("/user/register" ,"anon" ); map.put("/register.jsp" ,"anon" ); map.put("/**" ,"authc" );
1 2 3 4 5 6 7 8 9 10 11 12 @Bean public Realm getRealm () { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher (); credentialsMatcher.setHashAlgorithmName("md5" ); credentialsMatcher.setHashIterations(1024 ); CustomerRealm customerRealm = new CustomerRealm (); customerRealm.setCredentialsMatcher(credentialsMatcher); return customerRealm; }
授权 授予角色和权限 User <–> Role <–> Perm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Override protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) { String principal = (String) principalCollection.getPrimaryPrincipal(); UserService userService = (UserService) ApplicationContextUtil.getBean("userService" ); User user = userService.findRoleByUsername(principal); if (!CollectionUtils.isEmpty(user.getRoles())) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo (); user.getRoles().forEach(role -> { info.addRole(role.getName()); List<Perms> perms = userService.findPermsByRoleId2(role.getId()); if (!CollectionUtils.isEmpty(perms) && perms.get(0 ) != null ) { perms.forEach(perm -> info.addStringPermission(perm.getName())); } }); return info; } return null ; }
判断是否有角色和权限 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.xw.controller;@Controller @RequestMapping("order") public class OrderController { @RequiresRoles(value={"admin","user"}) @RequiresPermissions("user:update:01") @RequestMapping("save") public String save () { System.out.println("进入方法" ); return "redirect:/index.jsp" ; } }
启用缓存 将用户的角色和权限保存在缓存中,不用每次都去数据库查
EhCache
EhCache是shiro的默认缓存
导入依赖
1 2 3 4 5 <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-ehcache</artifactId > <version > 1.5.3</version > </dependency >
设置缓存管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Bean public Realm getRealm () { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher (); credentialsMatcher.setHashAlgorithmName("md5" ); credentialsMatcher.setHashIterations(1024 ); CustomerRealm customerRealm = new CustomerRealm (); customerRealm.setCredentialsMatcher(credentialsMatcher); customerRealm.setCacheManager(new EhCacheManager ()); customerRealm.setCachingEnabled(true ); customerRealm.setAuthenticationCachingEnabled(true ); customerRealm.setAuthenticationCacheName("authentication" ); customerRealm.setAuthorizationCachingEnabled(true ); customerRealm.setAuthorizationCacheName("authorization" ); return customerRealm; }
Redis 导入依赖
1 2 3 4 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
配置Redis
1 2 3 4 5 spring: redis: port: 6379 host: localhost database: 0
编写Redis缓存管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.xw.config.redis;import com.xw.utils.ApplicationContextUtil;import org.apache.shiro.cache.Cache;import org.apache.shiro.cache.CacheException;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;import java.util.Collection;import java.util.Set;public class RedisCache <k,v> implements Cache <k,v> { private String cacheName; public RedisCache () { } public RedisCache (String cacheName) { this .cacheName = cacheName; } @Override public v get (k k) throws CacheException { return (v) getRedisTemplate().opsForHash().get(this .cacheName, k.toString()); } @Override public v put (k k, v v) throws CacheException { System.out.println("put key: " +k); System.out.println("put value:" +v); getRedisTemplate().opsForHash().put(this .cacheName,k.toString(), v); return null ; } @Override public v remove (k k) throws CacheException { System.out.println("=============remove=============" ); return (v) getRedisTemplate().opsForHash().delete(this .cacheName,k.toString()); } @Override public void clear () throws CacheException { System.out.println("=============clear==============" ); getRedisTemplate().delete(this .cacheName); } @Override public int size () { return getRedisTemplate().opsForHash().size(this .cacheName).intValue(); } @Override public Set<k> keys () { return getRedisTemplate().opsForHash().keys(this .cacheName); } @Override public Collection<v> values () { return getRedisTemplate().opsForHash().values(this .cacheName); } private RedisTemplate getRedisTemplate () { RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate" ); redisTemplate.setKeySerializer(new StringRedisSerializer ()); redisTemplate.setHashKeySerializer(new StringRedisSerializer ()); return redisTemplate; } }
1 2 3 4 5 6 7 8 9 package com.xw.config.redis;public class RedisCacheManager implements CacheManager { @Override public <K, V> Cache<K, V> getCache (String cacheName) throws CacheException { System.out.println(cacheName); return new RedisCache <K, V>(cacheName); } }
开启缓存管理器的地方改成RedisCacheManager
simpleByteSource实现没有实现序列化,如果使用到了盐值,需要自定义盐值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 package com.xw.shiro.salt;public class MyByteSource implements ByteSource , Serializable { private byte [] bytes; private String cachedHex; private String cachedBase64; public MyByteSource (byte [] bytes) { this .bytes = bytes; } public MyByteSource (char [] chars) { this .bytes = CodecSupport.toBytes(chars); } public MyByteSource (String string) { this .bytes = CodecSupport.toBytes(string); } public MyByteSource (ByteSource source) { this .bytes = source.getBytes(); } public MyByteSource (File file) { this .bytes = (new MyByteSource .BytesHelper()).getBytes(file); } public MyByteSource (InputStream stream) { this .bytes = (new MyByteSource .BytesHelper()).getBytes(stream); } public static boolean isCompatible (Object o) { return o instanceof byte [] || o instanceof char [] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream; } public byte [] getBytes() { return this .bytes; } public boolean isEmpty () { return this .bytes == null || this .bytes.length == 0 ; } public String toHex () { if (this .cachedHex == null ) { this .cachedHex = Hex.encodeToString(this .getBytes()); } return this .cachedHex; } public String toBase64 () { if (this .cachedBase64 == null ) { this .cachedBase64 = Base64.encodeToString(this .getBytes()); } return this .cachedBase64; } public String toString () { return this .toBase64(); } public int hashCode () { return this .bytes != null && this .bytes.length != 0 ? Arrays.hashCode(this .bytes) : 0 ; } public boolean equals (Object o) { if (o == this ) { return true ; } else if (o instanceof ByteSource) { ByteSource bs = (ByteSource)o; return Arrays.equals(this .getBytes(), bs.getBytes()); } else { return false ; } } private static final class BytesHelper extends CodecSupport { private BytesHelper () { } public byte [] getBytes(File file) { return this .toBytes(file); } public byte [] getBytes(InputStream stream) { return this .toBytes(stream); } } }
把自定义Realm中认证地方的simpleByteSource改成自定义的MyByteSource