shiro相关
- 权限管理:一句话概述就是赋予用户不同角色从而拥有不同的功能.
- Apache Shiro
- Shiro 比 Spring Security更容易上手使用和理解,Shiro 可以不跟任何的框架或者容器绑定,可独立运
行,而Spring Security 则必须要有Spring环境, Shiro 可能没有 Spring Security 做的功能强大,但是
在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了 - Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等.
- Subject(用户): 访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;Subject 一词是一个专业术语,其基本意思是“当前的操作用户”。在程序任意位置可使用:Subject currentUser = SecurityUtils.getSubject() 获取到subject主体对象,类似 Employee user = UserContext.getUser()
- SecurityManager(安全管理器):它是 shiro 功能实现的核心,负责与后边介绍的其他组件(认证器/授权器/缓存控制器)进行交互,实现 subject 委托的各种功能。有点类似于SpringMVC 中DispatcherServlet 前端控制器,负责进行分发调度。
- Realms(数据源): Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。;可以把Realm 看成 DataSource,即安全数据源。执行认证(登录)和授权(访问控制)时,Shiro 会从应用配置的 Realm 中查找相关的比对数据。以确认用户是否合法,操作是否合理。
- Authenticator(认证器): 用于认证,从 Realm 数据源取得数据之后进行执行认证流程处理。
- Authorizer(授权器):用户访问控制授权,决定用户是否拥有执行指定操作的权限。
- SessionManager (会话管理器):Shiro 与生俱来就支持会话管理,这在安全类框架中都是独一无二的功能。即便不存在 web 容器环境,shiro 都可以使用自己的会话管理机制,提供相同的会话 API。
- CacheManager (缓存管理器):用于缓存认证授权信息等。
- Cryptography(加密组件): Shiro 提供了一个用于加密解密的工具包。
- Shiro 比 Spring Security更容易上手使用和理解,Shiro 可以不跟任何的框架或者容器绑定,可独立运
- 架包导入
<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.5.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.22</version> <scope>provided</scope> </dependency>
- 基本案例
@Test public void testLogin(){ //创建Shiro的安全管理器,是shiro的核心 DefaultSecurityManager securityManager = new DefaultSecurityManager(); //加载shiro.ini配置,得到配置中的用户信息(账号+密码) IniRealm iniRealm = new IniRealm("classpath:shiro-authc.ini"); securityManager.setRealm(iniRealm); //把安全管理器注入到当前的环境中 SecurityUtils.setSecurityManager(securityManager); //无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断 Subject subject = SecurityUtils.getSubject(); System.out.println("认证状态:"+subject.isAuthenticated()); //创建令牌(携带登录用户的账号和密码) UsernamePasswordToken token = new UsernamePasswordToken("dafei","666"); //执行登录操作(将用户的和 ini 配置中的账号密码做匹配) subject.login(token); System.out.println("认证状态:"+subject.isAuthenticated()); //登出 //subject.logout(); //System.out.println("认证状态:"+subject.isAuthenticated()); }
- 自定义realm
public class EmployeeRealm extends AuthorizingRealm { @Autowired private IEmployeeService employeeService; @Autowired private IRoleService roleService; @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //根据名字查询对应的employee对象 String username = (String)authenticationToken.getPrincipal(); Employee employee = employeeService.selectByUsername(username); if (employee != null){ return new SimpleAuthenticationInfo(employee, employee.getPassword(), this.getName()); } return null; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); Employee employee = (Employee) principalCollection.getPrimaryPrincipal(); System.out.println("employee = " + employee); // Subject subject = SecurityUtils.getSubject(); // Employee employee = (Employee)subject.getPrincipal(); //超管拥有所有的权限 if (employee.isAdmin()){ // info.addRole("admin"); List<Role> roles = roleService.listAll(); List<String> roleSns = new ArrayList<>(); for (Role role : roles) { roleSns.add(role.getSn()); } info.addRoles(roleSns); //*:*表示所有权限,不做控制,如果是employee:*就表示可以访问employee路径下的所有资源 info.addStringPermission("*:*"); }else { //普通员工就只拥有对应角色的权限 //将所有的角色加入info List<String> roleSns = employeeService.selectSnByEmpId(employee.getId()); info.addRoles(roleSns); //查询所有的权限加入info List<String> permissions = employeeService.selectPermissionsByEmpId(employee.getId()); info.addStringPermissions(permissions); } return info; } public void clearCache() { System.out.println("清除缓存数据"); Subject subject=SecurityUtils.getSubject(); // 调用子类去清理缓存 super.clearCache(subject.getPrincipals()); } }
- 如何在项目中集成shiro呢?
- 导入shiro相关的jar包
- 在web.xml中配置一个代理过滤器(org.springframework.web.filter.DelegatingFilterProxy)取名为shiroFilter,然后新建一个shiroFilter.xml的配置文件,将该配置文件导入到mvc.xml中,因为shiroFilter是不依赖spring容器的,而且其配置比较复杂(因此单独建立一个shiroFilter.xml),所以我们为了让spring帮我们管理,并且利于管理就使用了代理过滤器作为一个桥梁,当有请求访问时,就会被代理filter拦截,然后去spring容器中找到同名的(取名为shiroFilter)bean对象,所以在shiroFilter.xml中我们配置的真正的shiro的过滤器,名字一定要和代理过滤器中的名字一样,一般都为shiroFilter
- 在需要做权限控制的地方贴上注解@RequiresPermissions,注意了:这是shiro的注解,不是我们自定义的
- 自定好realm,在protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)方法中(重载的方法),将该对象拥有的所有角色和权限都加入进去,这里要额外注意下超管的处理,因为超管拥有所有的权限,所以要把所有的权限都给它
- 注意点:SecurityUtils.getSubject();这个方法在项目任何地方都可以使用,所以如果我们想通过编程式的方式来进行权限控制的话,可以直接在我们想要控制的资源那里通过if(subject.hasRole("hr"))这种形式来判断
- 当一个方法上贴了权限注解,就表示当前登录的用户必须拥有该权限才可以访问,如果还贴了@RequiresRoles
- @RequiresPermissions注解的灵活使用,这玩意儿不像我们自己定义的注解可以带上名字什么的,它就一个value属性,所以我们只能传入权限表达式,但是存在数据库还是展示在后台页面都是达不到需求的,因为@RequiresPermissions注解可以接受一个数组,所以我们默认传一个数组进去,第一个元素为表达式,第二个为name(给表达式取的名字),这样就可以达到我们的需求了,但同时必须注意要设置@RequiresPermissions注解的属性logical = Logical.OR,这个表示数组里面的元素有一个符合条件就行
- 如果我们的前端页面选择的是freemarker(shiro默认支持的是jsp,freemarker是不支持shiro的),那么我们就要自定义FreeMarkerConfigurer了,将shiro标签注册进freemarker中,示例如下
public class ShiroFreeMarkerConfig extends FreeMarkerConfigurer { @Override public void afterPropertiesSet() throws IOException, TemplateException { //继承之前的属性配置 super.afterPropertiesSet(); Configuration configuration = this.getConfiguration(); //注册shiro标签,在freemarker中使用的时候都是@shiro这种类型的 configuration.setSharedVariable("shiro",new ShiroTags()); } }
并且要在mvc.xml的配置中(freemarker)改成我们自定义的<bean class="cn.wolfcode.crm.util.ShiroFreeMarkerConfig">
- 然后就可以使用一系列标签了
- <@shiro.authenticated> 已认证通过的用户/@shiro.authenticated
- <@shiro.notAuthenticated>认证未通过的用户/@shiro.notAuthenticated
- <@shiro.principal property="name" />输出当前登录的用户名(这里的name属性是对应的登录用户对象里面的属性,不一定非得叫name)
- <@shiro.hasRole name="admin">是否拥有该角色/@shiro.hasRole
- <@shiro.hasPermission name="department:delete">是否拥有该权限/@shiro.hasPermission