shiro
shiro数据安全控制
提示:以下是本篇文章正文内容,下面案例可供参考
类似的产品:spring security, 学习成本过高,过于复杂,控制粒度细 shiro框架相对简单,足够应用在企业级项目中 springmvc中的拦截器,也可以做到安全管理,比shiro还简单
shiro安全框架能做什么: 主要的功能: 登录认证:shiro 登入 通过shiro做登录操作,如果没有登录成功,就跳转到登录页面 如果登录成功了,就跳转到指定的页面,而且还可以访问项目中 其他的敏感资源(项目中的所有的功能都能访问),就是用户访问 项目中的任何资源都要通过shiro检测是否登录
权限认证: 根据用户权限的不同,登录完后显示的主页菜单中菜单项也不同 比如: 超级管理员权限的账号,能够在主页面中显示所有的菜单项(10个) 普通权限的账号,指定显示3个菜单项shiro的全模块功能: primary concerns: Authentication:登录认证 AUthorization:授权认证 Session Management:session管理 可以用此session共享数据 shiro登出 Cryptography:加密管理
spportting features:支持的特性 web support:shiro支持web项目 caching:使用shiro的缓存机制,缓存数据 Concurrency:支持高并发 testing:支持测试 run as :支持java项目 remember me:支持记住我
shiro的工作流程图:参见shiro工作流程图.png
application code:应用程序的代码,最终对应的一个方法,此方法用来启动shiro; 这个方法可以用某一个线程调用 subject:subject英文原义就主题,标题,在shiro中理解成 current user 当前用户 可以理解成某一个用户在操作一个主题 某一个线程在操作一个主题 shiro securitymanager:shiro的安全管理器(安全管理中心) 管理所有subject realm:英文原义就是领域,范围, 可以理解成数据库中的数据,或文件中的数据,总之是数据源.
文字版的shiro的工作流程: 用户输入用户名和密码,通过shrio把用户的名和密码,存储给subject subject携带数据给安全管理中心,安全管理中心管理所有的subject 安全管理中心回调AuthorizingRealm类或实现Authorizer接口中的方法,有两个方法 登录认证:protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException 权限认证:protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) 在登录认证中指定如何从数据库中获取数据库的真实用户登录数据(realm),然后realm数据给安全管理中即可 在权限认证中指定如果从数据库中获取数据库的真实的权限数据(realm),然后把realm数据给安全管理中心即可 总之,用户写好回调的策略方法,由安全管理中心回调 回调方法是有顺序的先回调登录认证,后回调权限认证 最后由安全管理中心负责判断并跳转.
其实shiro的底层用的是动态代理: //环绕通知,能够控制业务方法的执行 public Object around(ProceedingJoinPoint pjp){ Object returnValue=null; try{ //前置通知 if(登录过){ returnValue=pjp.proceed();//调用目标方法 }else{ //跳转到登录页面 //页面的名称配置到spring_shiro.xml中 } //后置通知 }catch(Exception e){ //异常通知 }finally{ //最终通知 } return returnValue; }
在maven项目里的src/main/resources的文件家里创建一个conf文件夹,里面创建一个spring_shiro.xml文件里面代码如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--将Shiro的组件以bean的形式交给Spring管理 --> <bean id="lifeCycleBeanProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean> <!--Spring为shiro的bean创建代理对象 代理的方式: 1.jdk 2.cglib --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifeCycleBeanProcessor"> <!--表示强制使用cglib为其创建代理对象 --> <property name="proxyTargetClass" value="true"></property> </bean> <!--切面中需要的对象,也使用cglib来创建代理对象 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> <!--Shiro的安全中心 其中需要提供真实的用户信息. 需要加载realm --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="AuthRealm"></property> </bean> <!--自定义Realm 为安全中心提供信息 --> <bean id="AuthRealm" class="cn.tedu.shiro.AuthRealm"> <property name="credentialsMatcher" ref="authCredential"></property> </bean> <!--自定义加密算法 --> <bean id="authCredential" class="cn.tedu.shiro.AuthCredential"/> <!--权限认证的适配器 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"></property> </bean> <!--创建一个logoutFilter对象,shiro登出 --> <bean id="logoutFilter" class="cn.tedu.shiro.SystemLogoutFilter"> <property name="redirectUrl" value="/login.html"></property> </bean> <!-- shiro的过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!--配置安全中心 --> <property name="securityManager" ref="securityManager"></property> <!--指定登陆的地址 当用户没有登陆时.默认跳转该页面--> <property name="loginUrl" value="/login.html"></property> <!-- 注册shiro的过滤器 --> <property name="filters"> <map> <!-- 注册登出的过滤器给shiro过滤器 --> <entry key="logout" value-ref="logoutFilter"></entry> </map> </property> <!--过滤器链 --> <property name="filterChainDefinitions"> <value> <!--添加过滤信息 1.anon 表示放行 2.authc 表示拦截--> /user/login = anon /css/** = anon /font-awesome/** = anon /fonts/** = anon /head/** = anon /images/** = anon /js/** = anon /page/** = anon <!-- 请求user/logout地址,shiro会调用过滤器中的preHandle --> /user/logout=logout <!--/** 拦截所有的请求和静态资源文件 --> /index.html* = authc /index.jsp* = authc /** = authc </value> </property> </bean> </beans>3.创建两个类 a.此类继承自AuthoriaingRealm类 重写两个方法 登录认证 权限认证
package cn.tedu.shiro; import java.util.List; import javax.annotation.Resource; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import cn.tedu.dao.UserMapper; import cn.tedu.entity.User; public class AuthRealm extends AuthorizingRealm { @Resource private UserMapper userMapper; //权限认证的回调方法 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("权限认证"); // 得到用户对象 Subject subject = SecurityUtils.getSubject(); String loginName = subject.getSession().getAttribute("loginName").toString(); // 根据用户名查询角色信息 List<String> moduleName=userMapper.findModulesByLoginName(loginName); // List<String> roleList = userService.findRoleByUserName(username); /* * List<String> roleList = new ArrayList<String>(); roleList.add("用户管理"); * roleList.add("课程管理"); roleList.add("视频管理"); */ // 创建授权管理 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 传入授权管理的集合信息 info.addStringPermissions(moduleName); return info; } @Override // 登陆认证回调方法 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("登录认证"); UsernamePasswordToken loginToken =(UsernamePasswordToken) token; String loginName = loginToken.getUsername(); User user =userMapper.findUserByUserLoginName(loginName); /* * principal:主要的; 本金的; 最重要的; 资本的; 真实的对象 * credentials:凭证,证件; 表示真实的密码 * realmName:域,范围的名字 当前的realm */ AuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName()); return info; } }b.此类继承自SimpleCredentialsMatcher类 重写一个方法,指定密码的加密原则
package cn.tedu.shiro; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.SimpleCredentialsMatcher; /** * 这是一个加密的回调方法 * @author Mechrev * */ public class AuthCredential extends SimpleCredentialsMatcher{ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { //通过token 获取用户名和密码 UsernamePasswordToken userToken = (UsernamePasswordToken) token; /*String username = userToken.getUsername(); String password = String.valueOf(userToken.getPassword()); String encryptPassword = Encrypt.getMd5(password, username); userToken.setPassword(encryptPassword.toCharArray());*/ return super.doCredentialsMatch(userToken, info); } }分析用shiro登录和不用shiro登录的区别 不用shiro: select user_id from t_user where user_loginname=? and user_password=?
<!-- 非shiro登录 --> <select id="login" parameterType="User" resultType="java.lang.String"> select user_id from t_user where user_loginname=#{loginName} and user_password=#{password} </select> 优点:访问数据库一次,能完成登录业务 缺点:不能防护除登录外的其他的敏感资源,要想防护,必须借助spirng mvc拦截器 <!-- shiro登录 --> <select id="findUserByUserLoginName" parameterType="java.lang.String" resultType="User"> select user_id id, user_loginname loginName, user_password password from t_user where user_loginname=#{loginName} </select> 优点:访问数据库一次,能够完成登录,可以防护敏感资源 缺点:性能堪忧,因为每一次url请求资源,都要经过shiro的安全管理来判断是否是登录用户代码如下(示例):
data = pd.read_csv( 'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv') print(data.head())该处使用的url网络请求的数据。