1. 权限管理

1.1 什么是权限管理?

权限管理实现对用户访问系统的控制,按照安全规则或者安全策略,可以控制用户只能访问自己被授权的资源

权限管理包括用户身份认证和授权两部分,简称认证授权

1.2 什么是身份认证?

身份认证就是判断一个用户是否为合法用户的处理过程,最常用的方式就是通过核对用户输入和用户名和口令是否与系统中存储的一致,来判断用户身份是否正确

1.3 什么是授权?

授权,即访问控制,控制谁能访问哪些资源,主体进行身份认证后需要分配权限方可访问系统资源,对于某些资源没有权限是无法访问的

2. 什么是 shiro?

shiro 是一个功能强大且易于使用的 Java 安全框架,能够完成身份认证、授权、加密和会话管理等功能

2.1 shiro 的核心架构

  • Subject

    主体,外部应用与 subject 进行交互,subject 记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject 在 shiro 中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过 subject 进行认证授,而 subject 是通过 SecurityManager 安全管理器进行认证授权

  • SecurityManager

    安全管理器,对全部的 subject 进行安全管理,是 shiro 的核心。通过 SecurityManager 可以完成 subject 的认证、授权等,实质上 SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理。SecurityManager 是一个接口,继承了 Authenticator,Authorizer,SessionManager 这三个接口

  • Authenticator

    认证器,对用户身份进行认证,Authenticator 是一个接口,shiro 提供 ModularRealmAuthenticator 实现类,通过 ModularRealmAuthenticator 基本上可以满足大多数需求,也可以自定义认证器

  • Authorizer

    授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限

  • Realm

    领域,相当于 datasource 数据源,securityManager 进行安全认证需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 realm 就需要从数据库获取用户身份信息

  • SessionManager

    会话管理,shiro 框架定义了一套会话管理,它不依赖 web 容器的 session,所以 shiro 可以使用在非 web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录

  • SessionDAO

    会话 dao,是对 session 会话操作的一套接口,比如要将 session 存储到数据库,可以通过 jdbc 将会话存储到数据库

  • CacheManager

    CacheManager 即缓存管理,将用户权限数据存储在缓存,这样可以提高性能

  • Cryptography

    密码管理,shiro 提供了一套加解密的组件,方便开发,比如提供常用的散列、加解密等功能

3. shiro 中的认证

3.1 认证

身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确

3.2 shiro 认证中的关键对象

  • Subject:主体

    访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体

  • Principal:身份信息

    是主体(subject)进行身份认证的标识,标识必须具有唯一性, 如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)

  • credential:凭证信息

    是只有主体自己知道的安全信息,如密码、证书等

3.3 认证流程

3.4 认证开发

3.4.1 引入依赖

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.5.3</version>
</dependency>

3.4.2 创建配置文件

该配置文件用来书写系统中相关的权限数据,主要用于学习使用

[users]
xiaochen=123
zhangsan=456

3.4.3 开发认证代码

public class TestAuthenticator {public static void main(String[] args) {// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.给安全管理器设置 realmsecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));// 3.给 SecurityUtils 全局安全工具类设置安全管理器SecurityUtils.setSecurityManager(securityManager);// 4.获取认证主体Subject subject = SecurityUtils.getSubject();// 5.创建令牌UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");// 6.认证try {System.out.println("认证状态:" + subject.isAuthenticated());subject.login(token);System.out.println("认证状态:" + subject.isAuthenticated());} catch (Exception e) {e.printStackTrace();}}
}

3.5 自定义 Realm

自定义 Realm 的实现,即是将认证/授权数据来源转为数据库的实现

public class TestCustomerRealmAuthenticator {public static void main(String[] args) {// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.设置自定义realmsecurityManager.setRealm(new CustomerRealm());// 3.给 SecurityUtils 全局安全工具类设置安全管理器SecurityUtils.setSecurityManager(securityManager);// 4.获取认证主体Subject subject = SecurityUtils.getSubject();// 5.创建令牌UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");// 6.认证try {System.out.println("认证状态:" + subject.isAuthenticated());subject.login(token);System.out.println("认证状态:" + subject.isAuthenticated());} catch (Exception e) {e.printStackTrace();}}
}

自定义 Realm 代码实现

public class CustomerRealm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 1. 在token中获取用户名String principal = (String) authenticationToken.getPrincipal();// 2. 查询数据库,此处模拟数据库数据if ("xiaochen".equals(principal)) {// 参数1:正确的用户名// 参数2:正确的密码// 参数3:提供当前realm的名字SimpleAuthenticationInfo simpleAuthenticationInfo= new SimpleAuthenticationInfo("xianchen", "123", this.getName());return simpleAuthenticationInfo;}return null;}
}

3.6 明文加密

实际使用时,我们不可能把用户密码以明文形式显示,需要做加密处理

通常的加密方式是使用 md5 + salt + hash 散列的形式,校验过程:保存盐和散列后的值,在 shiro 完成密码校验

下面是使用 shiro 提供的 api 完成加密代码示例

public class TestShiroMD5 {public static void main(String[] args) {// 1. 使用md5加密// 参数1是明文密码Md5Hash md5Hash1 = new Md5Hash("123");// 打印加密后的密文// 结果:202cb962ac59075b964b07152d234b70System.out.println(md5Hash1.toHex());// 2. 使用md5+salt加密Md5Hash md5Hash2 = new Md5Hash("123", "X0*7ps");// 8a83592a02263bfe6752b2b5b03a4799System.out.println(md5Hash2.toHex());// 3. 使用md5+salt+hash散列加密Md5Hash md5Hash3 = new Md5Hash("123", "X0*7ps", 1024);// e4f9bf3e0c58f045e62c23c533fcf633System.out.println(md5Hash3.toHex());}
}

自定义 CustomerMd5Realm

public class CustomerMd5Realm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 1. 在token中获取用户名String principal = (String) authenticationToken.getPrincipal();// 2. 查询数据库,此处模拟数据库数据if ("xiaochen".equals(principal)) {// 参数1:正确的用户名// 参数2:正确的密码// 参数3:提供当前realm的名字// md5// return new SimpleAuthenticationInfo(principal, "202cb962ac59075b964b07152d234b70", this.getName());// md5+salt/*return new SimpleAuthenticationInfo(principal, "8a83592a02263bfe6752b2b5b03a4799", ByteSource.Util.bytes("X0*7ps"), this.getName());*/}return null;}
}

校验流程

public class TestCustomerMd5RealmAuthenticator {public static void main(String[] args) {// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.设置自定义realmCustomerMd5Realm realm = new CustomerMd5Realm();HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();// 使用MD5加密hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列次数hashedCredentialsMatcher.setHashIterations(1024);realm.setCredentialsMatcher(hashedCredentialsMatcher);securityManager.setRealm(realm);// 3.给 SecurityUtils 全局安全工具类设置安全管理器SecurityUtils.setSecurityManager(securityManager);// 4.获取认证主体Subject subject = SecurityUtils.getSubject();// 5.创建令牌UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");// 6.认证try {System.out.println("认证状态:" + subject.isAuthenticated());subject.login(token);System.out.println("认证状态:" + subject.isAuthenticated());} catch (Exception e) {e.printStackTrace();}}
}

4. shiro 中的授权

4.1 授权

授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的

4.2 关键对象

授权可简单理解为 who 对 what(which) 进行 How 操作:

  • Who,即主体(Subject),主体需要访问系统中的资源
  • What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为 t01 的商品为资源实例,编号为 001 的商品信息也属于资源实例
  • How,权限/许可(Permission),规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为 001 用户的修改权限等,通过权限可知道主体对哪些资源都有哪些操作许可

4.3 授权方式

基于角色的访问控制,以角色为中心进行访问控制

if(subject.hasRole("admin")){//操作什么资源
}

基于资源的访问控制,以资源为中心进行访问控制

if(subject.isPermission("user:update:01")){ //资源实例//对01用户进行修改
}
if(subject.isPermission("user:update:*")){  //资源类型//对01用户进行修改
}

4.4 权限字符串

权限字符串的规则是:资源标识符:操作:资源实例标识符,意思是对哪个资源的哪个实例具有什么操作,: 是资源/操作/实例的分割符,权限字符串也可以使用 * 通配符

例子:

  • 用户创建权限:user:create,或 user:create:*
  • 用户修改实例 001 的权限:user:update:001
  • 用户实例 001 的所有权限:user:*:001

4.5 授权编程实现

在之前 md5 加密的基础上,实现授权操作

自定义 CustomerMd5Realm

public class CustomerMd5Realm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {// 获取身份信息String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();// 根据身份信息(用户名)从数据库获取当前用户的角色以及权限信息SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();// 将数据库中查询的角色信息赋值给权限对象simpleAuthorizationInfo.addRole("admin");simpleAuthorizationInfo.addRole("user");// 将数据库中查询的权限信息赋值给权限对象simpleAuthorizationInfo.addStringPermission("user:*:01");simpleAuthorizationInfo.addStringPermission("product:create:02");return simpleAuthorizationInfo;}...
}

授权逻辑

public class TestCustomerMd5RealmAuthenticator {public static void main(String[] args) {...// 7. 认证用户进行授权if (subject.isAuthenticated()) {// 7.1 基于角色权限控制boolean hasRole = subject.hasRole("admin");System.out.println("角色校验:" + hasRole);// 7.2 基于多角色权限控制boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("admin", "user"));System.out.println("多角色校验:" + hasAllRoles);// 7.3 是否具有其中一个角色boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));for (boolean aBoolean : booleans) {System.out.println(aBoolean);}// 7.4 基于权限字符串的访问控制boolean permitted = subject.isPermitted("user:*:01");System.out.println("资源权限校验:" + permitted);// 7.5 分布具有哪些资源权限boolean[] permitted1 = subject.isPermitted("user:*:01", "order:*:10");for (boolean b : permitted1) {System.out.println(b);}// 7.6 同时具有哪些资源权限boolean permittedAll = subject.isPermittedAll("user:*:01", "product:*");System.out.println("多资源权限校验:" + permittedAll);}}
}

5. shiro 整合 SpringBoot

shiro 整合 SpringBoot 的思路如图所示:

引入 shiro 整合 SpringBoot 依赖

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.5.3</version>
</dependency>

5.1 认证

配置 shiro

@Configuration
public class ShiroConfig {// 1.创建shiroFilter,负责拦截所有请求@Beanpublic ShiroFilterFactoryBean getShiroFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 给filter设置安全管理器shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);// 配置系统受限资源HashMap<String, String> map = new HashMap<>();map.put("/user/login", "anon");  // anon设置为公共资源map.put("/user/register", "anon");map.put("/register.jsp", "anon");map.put("/**", "authc"); // authc表示请求这个资源需要认证和授权// 默认认证界面路径shiroFilterFactoryBean.setLoginUrl("/login.jsp");shiroFilterFactoryBean.setFilterChainDefinitionMap(map);return shiroFilterFactoryBean;}// 2.创建安全管理器@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("realm") Realm realm) {DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();// 给安全管理器设置RealmdefaultWebSecurityManager.setRealm(realm);return defaultWebSecurityManager;}// 3.创建自定义realm@Bean(name = "realm")public Realm getRealm() {CustomerRealm customerRealm = new CustomerRealm();// 修改凭证校验匹配器HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();// 设置加密算法为md5credentialsMatcher.setHashAlgorithmName("MD5");// 设置散列次数credentialsMatcher.setHashIterations(1024);customerRealm.setCredentialsMatcher(credentialsMatcher);return customerRealm;}
}
public class CustomerRealm extends AuthorizingRealm {@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {String principal = (String) authenticationToken.getPrincipal();UserSev userSev = (UserSev) ApplicationContextUtils.getBean("userSev");User user = userSev.findByUsername(principal);if (user != null) {return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),ByteSource.Util.bytes(user.getSalt()), this.getName());}return null;}
}

shiro 提供了多个默认的过滤器,我们可以用这些过滤器来控制指定 url 的权限

配置缩写 对应的过滤器 功能
anon AnonymousFilter 指定url可以匿名访问
authc FormAuthenticationFilter 指定url需要form表单登录,默认会从请求中获取usernamepassword,rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。
authcBasic BasicHttpAuthenticationFilter 指定url需要basic登录
logout LogoutFilter 登出过滤器,配置指定url就可以实现退出功能,非常方便
noSessionCreation NoSessionCreationFilter 禁止创建会话
perms PermissionsAuthorizationFilter 需要指定权限才能访问
port PortFilter 需要指定端口才能访问
rest HttpMethodPermissionFilter 将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释
roles RolesAuthorizationFilter 需要指定角色才能访问
ssl SslFilter 需要https请求才能访问
user UserFilter 需要已登录或“记住我”的用户才能访问

模拟认证、注册和退出过程

@Controller
@RequestMapping("user")
public class UserCon {@Autowiredprivate UserSev userSev;@RequestMapping("logout")public String logout(String username, String password) {// 获取主体对象Subject subject = SecurityUtils.getSubject();subject.logout();return "redirect:/login.jsp";}@RequestMapping("login")public String login(String username, String password) {// 获取主体对象Subject subject = SecurityUtils.getSubject();try {subject.login(new UsernamePasswordToken(username, password));return "redirect:/index.jsp";} catch (UnknownAccountException e) {System.out.println("用户名错误");} catch (IncorrectCredentialsException e) {System.out.println("密码错误");}return "redirect:/index.jsp";}@RequestMapping("register")public String register(User user) {try {userSev.register(user);} catch (Exception e) {return "redirect:/register.jsp";}return "redirect:/login.jsp";}
}
@Service
public class UserSev {@Autowiredprivate UserDao userDao;public void register(User user) {// 处理业务调用dao// 明文密码进行 md5 + salt + hash散列String salt = SaltUtils.getSalt();user.setSalt(salt);Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);user.setPassword(md5Hash.toHex());userDao.save(user);}public User findByUsername(String username) {return userDao.findByUserName(username);}
}

5.2 授权

第一种方式,通过页面标签授权

<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:hasAnyRoles name="user,admin"><li><a href="">用户管理</a><ul><shiro:hasPermission name="user:add:*"><li><a href="">添加</a></li></shiro:hasPermission><shiro:hasPermission name="user:delete:*"><li><a href="">删除</a></li></shiro:hasPermission><shiro:hasPermission name="user:update:*"><li><a href="">修改</a></li></shiro:hasPermission><shiro:hasPermission name="user:find:*"><li><a href="">查询</a></li></shiro:hasPermission></ul></li>
</shiro:hasAnyRoles>
<shiro:hasRole name="admin"><li><a href="">商品管理</a></li><li><a href="">订单管理</a></li><li><a href="">物流管理</a></li>
</shiro:hasRole>

第二种方式,代码方式授权

@RequestMapping("save")
public String save(){System.out.println("进入方法");//获取主体对象Subject subject = SecurityUtils.getSubject();//代码方式if (subject.hasRole("admin")) {System.out.println("保存订单!");}else{System.out.println("无权访问!");}//基于权限字符串//....return "redirect:/index.jsp";
}

第二种方式,注解方式授权

@RequiresRoles(value={"admin","user"})//用来判断角色  同时具有 admin user
@RequiresPermissions("user:update:01") //用来判断权限字符串
@RequestMapping("save")
public String save(){System.out.println("进入方法");return "redirect:/index.jsp";
}

这里只是做个演示,实际开发中,我们需要对授权数据持久化。需要三张表:用户表、角色表和权限表,用户表和角色表之间,角色表和权限表之间都是多对多的关系,需要建立一张关系表

修改自定义 Realm

public class CustomerRealm extends AuthorizingRealm {@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//获取身份信息String primaryPrincipal = (String) principals.getPrimaryPrincipal();System.out.println("调用授权验证: "+primaryPrincipal);//根据主身份信息获取角色 和 权限信息UserService userService = (UserService) ApplicationContextUtils.getBean("userService");User user = userService.findRolesByUserName(primaryPrincipal);//授权角色信息if(!CollectionUtils.isEmpty(user.getRoles())){SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();user.getRoles().forEach(role->{simpleAuthorizationInfo.addRole(role.getName());//权限信息List<Perms> perms = userService.findPermsByRoleId(role.getId());if(!CollectionUtils.isEmpty(perms)){perms.forEach(perm->{simpleAuthorizationInfo.addStringPermission(perm.getName());});}});return simpleAuthorizationInfo;}return null;}
}

5.3 shiro 缓存

使用缓存可以减轻 DB 的访问压力,从而提高系统的查询效率

5.3.1 整合 Ehcache

引入 ehcache 依赖

<!--引入shiro和ehcache-->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.5.3</version>
</dependency>

开启缓存

@Bean
public Realm getRealm(){CustomerRealm customerRealm = new CustomerRealm();//修改凭证校验匹配器HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();//设置加密算法为md5credentialsMatcher.setHashAlgorithmName("MD5");//设置散列次数credentialsMatcher.setHashIterations(1024);customerRealm.setCredentialsMatcher(credentialsMatcher);//开启缓存管理customerRealm.setCacheManager(new EhCacheManager());customerRealm.setCachingEnabled(true);//开启全局缓存customerRealm.setAuthenticationCachingEnabled(true);//认证认证缓存customerRealm.setAuthenticationCacheName("authenticationCache");customerRealm.setAuthorizationCachingEnabled(true);//开启授权缓存customerRealm.setAuthorizationCacheName("authorizationCache");return customerRealm;
}

5.3.2 整合 Redis

引入 redis 依赖

<!--redis整合springboot-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置 redis 连接,启动 redis 服务

spring.redis.port=6379
spring.redis.host=localhost
spring.redis.database=0

ehcache 提供了 EhCacheManager,而 EhCacheManager 实现了 CacheManager 接口,因此我们可以自定义一个 RedisCacheManager

public class RedisCacheManager implements CacheManager {@Overridepublic <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {System.out.println("缓存名称: "+cacheName);return new RedisCache<K,V>(cacheName);}
}

再自定义 Cache 接口实现

public class RedisCache<K,V> implements Cache<K,V> {private String cacheName;public RedisCache() {}public RedisCache(String cacheName) {this.cacheName = cacheName;}@Overridepublic V get(K k) throws CacheException {System.out.println("获取缓存:"+ k);return (V) getRedisTemplate().opsForHash().get(this.cacheName,k.toString());}@Overridepublic V put(K k, V v) throws CacheException {System.out.println("设置缓存key: "+k+" value:"+v);getRedisTemplate().opsForHash().put(this.cacheName,k.toString(),v);return null;}@Overridepublic V remove(K k) throws CacheException {return (V) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());}@Overridepublic v remove(k k) throws CacheException {return (v) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());}@Overridepublic void clear() throws CacheException {getRedisTemplate().delete(this.cacheName);}@Overridepublic int size() {return getRedisTemplate().opsForHash().size(this.cacheName).intValue();}@Overridepublic Set<k> keys() {return getRedisTemplate().opsForHash().keys(this.cacheName);}@Overridepublic Collection<v> values() {return getRedisTemplate().opsForHash().values(this.cacheName);}private RedisTemplate getRedisTemplate(){RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());return redisTemplate;}// 封装获取 redisTemplateprivate RedisTemplate getRedisTemplate(){RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());return redisTemplate;}
}

我们还需要对 salt 作序列化实现,由于 shiro 中提供的 SimpleByteSource 实现没有实现序列化,所以我们需要自定义 salt 序列化实现

 // 自定义 salt 实现,实现序列化接口public class MyByteSource extends SimpleByteSource implements Serializable {public MyByteSource(String string) {super(string);}}

在 realm 中使用自定义 salt

public class MyByteSource implements ByteSource, Serializable {private  byte[] bytes;private String cachedHex;private String cachedBase64;//加入无参数构造方法实现序列化和反序列化public MyByteSource(){}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);}}
}

shiro 学习笔记相关推荐

  1. Shiro学习笔记_02:shiro的认证+shiro的授权

    Shiro 学习笔记 本文基于B站UP主[编程不良人]视频教程[2020最新版Shiro教程,整合SpringBoot项目实战教程]进行整理记录,仅用于个人学习交流使用. 视频链接:https://w ...

  2. Shiro学习笔记(三)源码解析

    Shiro作为轻量级的权限框架,Shiro的认证流程是怎样的一个过程. 如果没有对Shiro进行了解的话,建议先对Shiro学习笔记(一)学习一下Shiro基本的组 成. 1,几大重要组件解析 1.1 ...

  3. Shiro学习笔记四(Shiro集成WEB)

    这两天由于家里出了点事情,没有准时的进行学习.今天补上之前的笔记 -----没有学不会的技术,只有不停找借口的人 学习到的知识点: 1.Shiro 集成WEB 2.基于角色的权限控制 3.基于权限的控 ...

  4. Shiro学习笔记-----小组开发前后端分离项目---登陆认证盐值加密

    当下我正在参与的学校前后端分离的实习项目,有了登录系统的需求,于是我在了解到Shiro是当下主流的安全框 架后,毅然决然开启了学习Shiro的旅程 首先来谈谈什么是Shiro,Apache Shiro ...

  5. shiro学习笔记(三)知识整合

    参考 小D课堂 二当家小D老师的笔记 小滴课堂官网:https://xdclass.net/#/index 官网 第1章 Shiro权限实战课程介绍 第1集 Shiro权限实战课程介绍 简介:讲解为什 ...

  6. [shiro学习笔记]第二节 shiro与web融合实现一个简单的授权认证

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39933993 shiro官网: http://shiro.apache.org/ sh ...

  7. 文件用户Apache shiro学习笔记+ spring整合shiro (一)

    改章节朋友在青岛游玩的时候突然想到的...这两天就有想写几篇关于文件用户的博客,所以回家到之后就奋笔疾书的写出来发表了 Apache Shiro官网:http://shiro.apache.org/ ...

  8. shiro学习笔记:remeberMe,多次登录锁死账号

    关于shiro的rememberme的实现,再之前我们是使用cookie实现的,这里也是一样,原理都是相同的: 不过因为用到了shiro框架,因此需要再shiro中配置cookie以及缓存等,以及管理 ...

  9. [尚硅谷22版shiro]学习笔记

    Shiro 概述 shiro是什么 Apache Shiro 是一个功能强大且易于使用的 Java 安全(权限)框架.Shiro 可以完成:认证.授权.加密.会话管理.与 Web 集成.缓存 等.借助 ...

最新文章

  1. 只有15亩起源的新发地,却造就了32个亿万商家,1000多个千万商家!
  2. “python不是内部命令或外部命令,也不是可执行程序解决方案”的解决方案
  3. FastJson 打Release 包解析失败
  4. 【Python进阶】Python进阶专栏栏主自述:不忘初心,砥砺前行
  5. OpenGL Overdraw Count透支数的实例
  6. java nio proactor_reactor模式与java nio
  7. 有关国土的几个重大项目
  8. android语法高亮插件,安卓语法高亮编辑器 HighlightTextEditor
  9. 06Matplotlib数据可视化--6.2散点图
  10. python 迭代器 生成器_Python迭代器和生成器
  11. [转载] python价值算法_PangRank算法原理及其Python实现
  12. linux内存管理_架构师必读:Linux 的内存分页管理
  13. python获取视频缩略图_Python代码生成视频的缩略图的实例讲解
  14. exe文件打不开应该怎么办?
  15. 群晖NAS系统DSM入门
  16. 以太网测试仪的RFC2544测试你了解吗?
  17. c#html表格样式大全,table完美css样式,table的基本样式,table样式
  18. 人工智能系列之基本框架梳理
  19. 陶哲轩实分析 命题7.4.3 (级数的重排) 证明
  20. 软考中高项学员:2016年4月6日作业

热门文章

  1. 永葆年轻的小虫子 大众甲壳虫1.2T
  2. 华为手机鸿蒙系统测评,华为发布的鸿蒙系统到底有多牛(让理想成为现实)
  3. 数学:定积分和数列和互相转化
  4. 【菠萝狗四足机器人】二次开发教程--第一章 【简介与开发环境搭建】
  5. JVM 面试 4 连炮,你能顶住么?
  6. 俞敏洪:我和马云就差了8个字
  7. 买社保有哪些好处?社保好还是商业保险好?社保和商业保险的区别?
  8. 混沌工程是什么_平静中的混沌:什么是混沌工程?
  9. 《设计模式之禅》笔记
  10. 多功能计算机使用教程,腾讯tim使用教程计算机应用知识