Apache Shiro是Java的一个安全框架,Shiro可以帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存等。
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。
从以上也可以看出,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。
一、
1、Hibernate配置对象的关系
权限架构,部门+用户+用户扩展信息+角色+模块
2、部门的自关联
<many-to-one name="PARENT_ID" class="Dept" column="PARENT_ID"></many-to-one>
name实体中的配置的对象属性
class对应实体对象,写全路径my.domain.Dept有时候简写Dept,必须配置package
column对于数据库表中的外键
3、部门和用户,一对多
<set name="users" cascade="all" inverse="true">
<key column="DEPT_ID"/>
<ont-to-many class="User"/>
</set>
Set标签,是个集合,在实体对象中配置Set<T> users
name属性在实体中配置的集合的名称
cascade级联,级联保护,删除(save-update,delete,delete-orphan,all,all-delete-orphan)
inverse 交给另外一方来维护
key 外键
one-to-many.class 一对多具体调用的实例
cascade和inverse区别
Cascade主要用于级联操作(如:级联添加,删除)
Inverse主要用于控制权是否要反转,一般将控制权放在多方
True代表控制权要反转,就交给多方维护,在一方加,效率会提高
当删除部门时,记录删除部门下的所有用户
Inverse="false"是默认值可以不配
Update user set deptId=null where deptId=12;//解决父子关系
Delete from user where deptId is null;//先干掉孩子
Delete from dept where id =12;//自杀
如果inverse="true" 代表多方维护关系
Delete from user where deptId = 12;//自杀
Delete from dept where id = 1;//父部门在子部门销毁后销毁
False代表维护,是默认值
4、用户和部门,多对一,双向关联
<many-to-one name="dept" class="Dept" column="DEPT_ID"/>
Name配置关联的对象
Class调用的实体对象
Column 外键
用户和用户扩展信息,一对一,单向关联
<one-to-one name="userinfo" class="UserInfo" cascade="all"/>
Name配置实体中声明的对象
Class调用的实体对象
Cascade级联
将用户,用户扩展信息表中的主键生成策略:assigned
在service中将用户,用户扩展的id设置为同一个取值 用主键一对一
5、用户和角色,多对多
<set name="roles" table="ROLE_USER_P">
<key column="USER_ID"></key>
<many-to-many class="Role" column="ROLE_ID" order-by="ORDER_NO"></many-to-many>
</set>
Name在实体配置关联对象
Table 多对多,配置中间表
Key 外键
many-to-many 多对多,class配置关联对象,column外键,order-by排序
没有配置级联,inverse(删除角色不影响用户)
6、角色和 用户,多对多
<set name="users" table="ROLE_USER_P">
<key column="ROLE_ID"></key>
<many-to-many class="User" column="USER_ID"></many-to-many>
</set>
byte/short/int/char/varchar 执行效率
BYTE>SHORT>INT>CHAR>VARCHAR
id使用varchar-》分布式
Mysql数据库设计主键的时候使用int并带上mysql数据库自增
char和varchar的区别
char长度固定,执行效率更高,可能造成空间浪费
varchar长度不固定 剩余的空间会进行回收,运行效率低
角色模块添加
private Set<Module> modules = new HashSet<Module>(0);
还要在hibernate.cfg.xml中添加映射文件
<mapping resource="classpath:my/domain/Module.hbm.xml"></mapping>
Shiro是apache的安全框架,用于解决系统的认证和授权问题,同时提供了会话管理,数据加密机制
需要关注
1、如何获得Subject
2、如何定义一个符合规定的Realm域(密码比较器的定义也要做)
Shiro使用步骤
1、导入jar包
maven
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>${shiro.version}</version>
</dependency>
web工程
shiro-all
shiro-core
shiro-guice
2、过滤器配置
web.xml中一个filter 以一当十
<!-- Shiro核心控制器 一定放在struts2过滤器之前 否则action无法创建-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3、产生代理类
<!-- 使aspectJ注解起作用:自动为匹配的类生成代理对象 -->
<!-- shiro安全框架产生代理子类的方式,使用cglib方式 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
4、在applicationContext.xml文件中加载shiro配置文件
<import resource="classpath:applicationContext-shiro.xml"></import>
5、编写密码比较器
1、添加utils
2、Md5Hash算法
//高强度加密算法,不可逆
public static String md5(String password, String salt){
return new Md5Hash(password,salt,2).toString();
}
public static void main(String[] args) {
System.out.println(new Md5Hash("123456","tony",2).toString());
}
3、密码比较器
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher{
//密码比较的方法 token代表用户在界面输入的用户名和密码 info代表从数据库中得到的加密密码
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//向下转型
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2、将用户在界面输入的原始密码加密 数组的toString返回数组的地址
Object pwd = Encrypt.md5(new String(upToken.getPassword()), upToken.getUsername());
//3、取出数据库中加密的密码
Object dbPwd = info.getCredentials();
return this.equals(pwd, dbPwd);
}
}
配置
<!-- 设置密码加密策略 md5hash -->
<bean id="passwordMatcher" class="my.shiro.CustomCredentialsMatcher"/>
6、编写自定义Realm域
在UserService接口中加入新的方法
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
//授权 当jsp页面出现shiro标签时,就会执行授权方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
System.out.println("授权");
User user = (User) pc.fromRealm(this.getName()).iterator().next();//根据realm的名字去找对应的realm
Set<Role> roles = user.getRoles();//对象导航
List<String> permissions = new ArrayList<String>();
for(Role role:roles) {
//遍历每个角色
Set<Module> modules = role.getModules();//得到每个角色下的模块列表
for(Module m :modules) {
permissions.add(m.getName());
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissions);//添加用户的权限
return info;
}
//认证 token代表用户在界面上输入的用户名和密码
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证");
//1、向下转型
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2、调用业务方法,实现根据用户名查询
String hql = "from User where userName=?";
List<User> list = userService.find(hql, User.class, new String[] {upToken.getUsername()});
if(list!=null && list.size()>0) {
User user = list.get(0);
AuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info;//此处如果返回,就会进入到密码比较器
}
return null;//就会出现异常
}
Hibernate中实现数据检索的5种方式
1、对象导航(通过关联级别的数据检索方式加载数据),前提:事先配好关联关系
2、通过对象OID加载(get/load方法)
3、通过HQL语句
4、通过SQL语句
5、通过QBC语句
7、配置AuthReal域
<!-- 自定义权限认证 -->
<bean id="authRealm" class="my.shiro.AuthRealm">
<property name="userService" ref="userService"/>
<!-- 自定义密码加密算法 -->
<property name="credentialsMatcher" ref="passwordMatcher"/>
</bean>
8、登录操作 loginAction_login
if(UtilFuns.isEmpty(username)) {
return "login";
}
try {
//1、得到subject
Subject subject = SecurityUtils.getSubject();
//2、调用登录方法
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
subject.login(token);//当这行代码执行的时候,就会自动跳入到AuthRealm中认证方法
//3、登录成功时,就从Shiro中取出用户的登录信息
User user = (User) subject.getPrincipal();
//4、将用户放入session域中
session.put(SysConstant.CURRENT_USER_INFO, user);
} catch (Exception e) {
e.printStackTrace();
request.put("errorInfo","用户名或密码错误");
return "login";
}
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher{
//密码比较的方法 token代表用户在界面输入的用户名和密码 info代表从数据库中得到的加密密码
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//向下转型
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2、将用户在界面输入的原始密码加密 数组的toString返回数组的地址
Object pwd = Encrypt.md5(new String(upToken.getPassword()), upToken.getUsername());
//3、取出数据库中加密的密码
Object dbPwd = info.getCredentials();
return this.equals(pwd, dbPwd);
}
}
15、授权
当jsp页面上出现Shiro标签时就会执行AuthRealm域中的授权方法
1、引入Shiro标签
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
2、使用shiro标签进行授权判断
<shiro:hasPermission name="系统首页">
<span id="topmenu" onclick="toModule('home');">系统首页</span><span id="tm_separator"></span>
</shiro:hasPermission>
<shiro:hasPermission name="货运管理">
<span id="topmenu" onclick="toModule('cargo');">货运管理</span><span id="tm_separator"></span>
</shiro:hasPermission>
<shiro:hasPermission name="统计分析">
<span id="topmenu" onclick="toModule('stat');">统计分析</span><span id="tm_separator"></span>
</shiro:hasPermission>
<shiro:hasPermission name="基础信息">
<span id="topmenu" onclick="toModule('baseinfo');">基础信息</span><span id="tm_separator"></span>
</shiro:hasPermission>
<shiro:hasPermission name="系统管理">
<span id="topmenu" onclick="toModule('sysadmin');">系统管理</span>
</shiro:hasPermission>
<shiro:hasPermission name="流程管理">
<span id="topmenu" onclick="toModule('activiti');">流程管理</span>
</shiro:hasPermission>
ehcache-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
</ehcache>