shiro权限笔记
shiro框架运行流程
认证:系统提供的用于识别用户身份的功能,通常就是登录功能。----让系统知道你是谁??
授权:系统提供的为用户分配访问系统某些功能的能力。----让系统知道你能做什么??
官网:http://shiro.apache.org/
shiro运行流程
Application Code:应用程序代码,由开发人员负责开发
Subject:主体,当前用户
SecurityManager:安全管理器,由shiro框架提供,可以管理所有的主体
Realm:安全数据桥,类似于系统中的Dao,负责访问安全数据(权限、角色、用户)
shiro框架核心功能
1、认证
2、授权
3、会话管理
4、加密
shiro框架提供的权限控制方式
1、URL拦截
2、方法注解
3、页面标签
4、代码级别
涉及的jar包
拦截参数
Shiro 的默认Filter 对应的类
filter过滤器别名 |
描述 |
类路径 |
anon |
匿名过滤器 |
|
authc |
认证后过滤器 |
|
perms |
权限过滤器 |
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
port |
|
|
rest |
|
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
noSessionCreation |
|
|
authcBasic |
|
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
roles |
角色过滤器 |
|
ssl |
安全协议过滤器 |
|
user |
用户过滤器 |
|
logout |
退出过滤器 |
anon: 例如/admins/**=anon 没有参数,表示可以匿名使用。
authc: 例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
roles: 例如/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms:例如/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest: 例如/admins/user/**=rest[user],根据请求的方法,相当于
/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port: 例如/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?号后面的参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl: 例如/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user: 例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
注:anon,authcBasic,auchc,user是认证过滤器,
perms,roles,ssl,rest,port是授权过滤器
/**代表所有子包都能匹配
web.xml配置
<!-- spring提供用于整合shiro的过滤器 --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter>
使用shiro提供的方式进行认证
/* * 用户登录的方法-shiro提供的方式 */ public String login() { // 从session中获取自动生成验证码 String key = (String) ServletActionContext.getRequest().getSession().getAttribute("key"); // 判断输入的验证码是否为空 if (StringUtils.isNotBlank(checkcode) && checkcode.equals(key)) { //获得一个Subject对象 Subject subject = SecurityUtils.getSubject();//用户认证状态为“未认证” //用户名密码令牌 AuthenticationToken token =new UsernamePasswordToken(model.getUsername(), MD5Utils.md5(model.getPassword()));//密码加密,和数据库对应 try{ subject.login(token); //获取签名对象 User user = (User) subject.getPrincipal(); // 将对象写回浏览器的session ServletActionContext.getRequest().getSession().setAttribute("loginUser", user); return HOME; }catch (UnknownAccountException e) {//shiro框架提供的异常,通过安全管理器捕获,账号不存在异常(原因是认证方法返回的是null) e.printStackTrace(); return LOGIN; }catch (IncorrectCredentialsException e) {//shiro框架提供的异常,通过安全管理器捕获,密码错误异常 e.printStackTrace(); return LOGIN; } } else { // 验证码错误,返回登录页面 this.addActionError(this.getText("checkcodeerror")); return LOGIN; } }
自定义realm,实现认证和授权方法
/** * 自定义realm */ public class BosRealm extends AuthorizingRealm { // 注入dao @Autowired private IUserDao userDao; /** * 认证方法 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 获取令牌(action传过来的token),强转成UsernamePasswordToken UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; // 得到页面输入的用户名 String username = usernamePasswordToken.getUsername(); // 根据用户名到数据库中查询密码 User user = userDao.findUserByUsername(username); if (username == null) { return null; } String password = user.getPassword(); Object principal = user;// 签名对象 Object credentials = password;// 数据库中的密码 String realmName = this.getName();// 当前realm的名称 AuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName); return info; }
JdbcRealm加盐,MD5加密(纯数字的不安全)
实现加密方式:MD5(用户名+密码)或者MD5(MD5(用户名+密码))
第六步:在自定义Realm中实现授权方法
/** * 授权方法 */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //简单授权信息对象 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermission("admin");//为当前用户授权--admin权限 info.addRole("admin");//为当前用户授予admin角色 User user = (User) principals.getPrimaryPrincipal(); //TODO 后期需要连接数据库,查询当前登录人对应的实际的权限 return info; }
shiro权限控制注解方式
在applicationContext.xml中开启shiro注解支持
<!-- 开启shiro注解支持 --> <!-- 自动代理 --> <bean id="defaultAdvisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <!--强制使用cglib创建action的代理, 不配置,spring默认是使用JDK代理, 默认情况下,action有接口就会使用JDK代理 --> <property name="proxyTargetClass" value="true"></property> </bean> <!-- 切面类 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"></bean>
在Action的方法上加入shiro提供的注解
/* * 批量作废功能 */ @RequiresPermissions(value="delete")//执行这个方法,需要有delete这个权限 public String deleteBatch() { userService.delete(id); return LIST; }
修改BaseAction的构造方法(判断是JDK代理还是cglib代理)
ParameterizedType genericSuperclass =null; Type genericSuperclass2 = this.getClass().getGenericSuperclass(); if(genericSuperclass2 instanceof ParameterizedType){ //this为JDK创建的代理对象 genericSuperclass =(ParameterizedType) this.getClass().getGenericSuperclass(); }else{ //this为cglib创建的代理对象 genericSuperclass =(ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass(); }
创建权限模块数据模型(权限表、角色表、用户表、角色权限关系表、用户角色关系表)
用户和权限不直接建立关系,是为了方便后期授权