shiro
前言
shiro数据安全控制
提示:以下是本篇文章正文内容,下面案例可供参考
一、shiro是什么
shiro安全框架: shiro是做网页数据安全的一种框架,比如,在访问某个网站时没有登录的情况下是不能访问别的网页类似的产品:spring security, 学习成本过高,过于复杂,控制粒度细
shiro框架相对简单,足够应用在企业级项目中
springmvc中的拦截器,也可以做到安全管理,比shiro还简单
shiro安全框架能做什么:
主要的功能:
登录认证:shiro 登入
通过shiro做登录操作,如果没有登录成功,就跳转到登录页面
如果登录成功了,就跳转到指定的页面,而且还可以访问项目中
其他的敏感资源(项目中的所有的功能都能访问),就是用户访问
项目中的任何资源都要通过shiro检测是否登录
权限认证:
根据用户权限的不同,登录完后显示的主页菜单中菜单项也不同
比如:
超级管理员权限的账号,能够在主页面中显示所有的菜单项(10个)
普通权限的账号,指定显示3个菜单项
二.shiro使用流程
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;
}
三、使用步骤
1.配置文件
1.导入jar包 <!-- Apache Shiro 权限依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.3</version>
</dependency>
<!-- spring aop 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
在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的安全管理来判断是否是登录用户
2.读入数据
代码如下(示例):
data = pd.read_csv(
'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())
该处使用的url网络请求的数据。