Web环境下使用Shiro进行授权以及md5密码加密
md5加密
md5加密不可逆,但是简单加密不安全,会被收集起来。所以可以加盐以及多次迭代加密
shiro使用md5加密:
- 在shiro.xml中配置凭证匹配器并且在我们自定义realm中将其注入此匹配器
- 保存员工密码时将其md5加密,此处用用户名做盐。
- 在我们realm注入凭证匹配器后,realm在认证时会自动将token中的凭证加盐加密,然后再和安全数据源中的密码进行匹配
shiro授权
- shiro授权的javaSE程序
自定义的realm中有两个方法,一个方法是认证相关,认证后返回认证信息。另一个方法对认证后的主体可以授权(添加角色或权限),返回授权信息。然后在测试程序中可以获取到当前主体subject,我们需要先认证再判断是否拥有某些角色或者权限,subject中有些方法进行判断授权信息
自定义realm的认证和授权方法
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
}
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
}
判断当前主体是否有某种角色或权限
System.out.println(subject.hasRole("role1"));
System.out.println(subject.isPermitted("user:delete"));
注意:通过底层源代码发现在DelegatingSubject中的login()方法用于认证,此方法中不会执行MyRealm中的授权方法。另一个方法hasRole()或者ispermitted等判断权限的方法会进行授权,这时才会执行MyRealm中的授权方法,二者的流程对比
//认证,login方法
DelegatingSubject->AuthenticatingSecurityManager->ModularRealmAuthenticator->AuthenticatingRealm->MyRealm中的doGetAuthenticationInfo()
//授权,hasRole等授权信息判断方法
DelegatingSubject->AuthorizingSecurityManager->ModularRealmAuthorizer->AuthorizingRealm->MyRealm中的doGetAuthorizationInfo()
- crm中使用shiro授权
- 添加依赖,之前已经添加
- 在shiro.xml中配置AuthorizationAttributeSourceAdvisor,该bean会为所有贴有RequiresPermissions注解的controller类做aop,采用cglib动态代理,通过此代理在用户访问某些controller资源时shiro就会判断当前认证主体是否拥有权限去访问,如果没有抛出无权限500异常。此外如果已经贴了此注解的controller,现在在spring容器中获取的是代理类而不是真实类。
<!--开启shiro授权注解-->
<!--该注解会对所有贴有shiro注解的类进行aop动态代理(cglib),织入在进入方法前先进行权限检查的代码-->
<!--推论:现在凡是贴有requrespermissions注解的controller类现在都被动态代理了
当前容器中管理的是符合条件的controller的代理类
-->
<!--如果没有相应权限,抛出UnauthorizedException,统一异常处理中跳到权限不足界面即可-->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
- 改造controller中的标签,全部换为@RequiresPermissions,里面需要value数组做权限表达式,权限表达式格式‘user:list’,然后定义逻辑规则,是’与’还是’或’
- 改造加载权限方法,还是需要通过controller注解获取所有的bean,然后遍历,通过AopUtils.isCglibProxy(controllerBean)判断当前controller是否是代理类,是的话在遍历方法取出权限信息封装到Permission对象,然后把保存到数据库
- 认证主体的授权操作需要在我们自定义realm方法中完成,此处我们开始开发doGetAuthorizationInfo()方法,首先可以通过参数获取身份principal,此身份就是认证方法最后传的employee.拿到身份我们可以通过数据库查询得到该身份所拥有的权限和角色,info添加角色和权限,最后返回info即可完成授权。推论:每次访问一个新的url都要完成一次授权操作,都要执行一次MyRealm中的doGetAuthorizationInfo()方法,该方法每次都要操作数据库获取当前主体角色和权限,期待下边ehcache缓存
info.addRoles(roles);
info.addStringPermissions(permissions);
- 在mvc.xml的统一异常处理中,处理无权限异常统一跳转到无权限界面
Shiro中使用Ehcache做缓存
注意此缓存的效果就是缓存数据库中查询的角色和权限(经常访问但不经常修改),这样每次访问url都不用操作数据库,其他数据无法被缓存。
- 配置
- 引入ehcache,配置EhCacheManagerFactoryBean,id是ehCacheManager
- 引入shiro的ehcache,配置EhCacheManager,id是cacheManager,里边注入ehCacheManager
- 在之前的securityManager中注入cacheManager
shiro中页面标签
有时需要在页面中使用shiro标签,如果没有权限就不应该显示某些内容
- 配置
默认freemarker中不支持shiro标签,我们之前配置的freemarker解析器是FreeMarkerConfigurer,无法满足我们需要,自己写类继承于它,为其拓展shiro标签功能
自定义configurer
//freemarker默认不支持shiro标签,因此我们自定义freemarker解析器拓展其功能,注意在xml中需要更改
public class CRMFreemarkerConfigurer extends FreeMarkerConfigurer {
public void afterPropertiesSet() throws IOException, TemplateException {
super.afterPropertiesSet();
Configuration configuration = getConfiguration();
//向freemarker中注册新标签
configuration.setSharedVariable("shiro",new ShiroTags());
}
}
修改mvc.xml中对freemarker的配置
<bean class="cn.wolfcode.crm.shiro.config.CRMFreemarkerConfigurer">
<!-- 配置freemarker的文件编码 -->
<property name="defaultEncoding" value="UTF-8" />
<!-- 配置freemarker寻找模板的路径 -->
<property name="templateLoaderPath" value="/WEB-INF/views/" />
</bean>
- 使用常见页面标签
- principal标签:输出当前用户信息,通常为登录帐号信息 <@shiro.principal property=“name” />
- hasRole标签:验证当前用户是否属于该角色 <@shiro.hasRole name=”admin”>Hello admin!/@shiro.hasRole
- hasPermission标签:验证当前用户是否拥有该权限 ,<@shiro.hasPermission name=“order:*”>订单/@shiro.hasPermission