Java岗大厂面试百日冲刺【Day43】—— Shrio1
本文已获得原作者 _陈哈哈 授权并经过重新整理规划后发布。
本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识、集合容器、并发编程、JVM、Spring全家桶、MyBatis等ORMapping框架、MySQL数据库、Redis缓存、RabbitMQ消息队列、Linux操作技巧等。
面试题1:你来简单介绍一下Shiro框架吧?
Apache Shiro是Java的一个安全框架。使用Shiro可以非常容易的开发出足够好的应用。其不仅可以用在JavaSE环境,也可以用在JavaEE环境。Shiro可以帮助我们完成功能:认证、授权、加密、会话管理、与Web集成、缓存
等功能。
Shiro包括三个核心组件:Subject,SecurityManager和Realm。
Subject
:即当前操作用户。Subject在shiro中是一个接口,定义了很多认证授权的方法,外部程序通过Subject进行认证授权,而Subject通过SecurityManager进行认证授权。其实SecurityManager才是实际的执行者。SecurityManager
:安全管理器,所有与安全有关的操作都会与SecurityManager交互,且管理着所有的Subject,是shiro的核心,负责与shiro其他组件进行交互,如通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。SecurityManager也是一个接口,继承了Authenticator,Authorizer,SessionManager三个接口Realm
:Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。Shiro从Realm获取安全数据(如用户,角色,权限);也就是说SecurityManager要验证用户身份或操作权限,需要从Realm获取相应数据来判断(用户是否能登录,是否拥有什么权限等)。
追问1:Shiro常见的权限控制方式有哪几种?
- 方法注解权限控制
- 页面标签权限控制
- 代码级别权限控制
- URL级别权限控制
1. 方法注解权限控制:
基于代理技术实现,首先要在spring配置文件中进行声明开启shiro注解,然后在代码方法上用注解声明调用该方法需要什么权限。
<!-- 开启shiro框架注解支持 -->
<bean id="defaultAdvisorAutoProxyCreator"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<!-- 必须使用cglib方式为Action对象创建代理对象 -->
<property name="proxyTargetClass" value="true"/>
</bean>
<!-- 配置shiro框架提供的切面类,用于创建代理对象 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/>
然后在方法上声明:
@RequiresPermissions("user-delete")
//执行这个方法,需要当前用户具有user-delete这个权限
public String deleteUser(){
staffService.deleteUser(user_name);
return LIST;
}
2. 页面标签权限控制:
首先要在jsp页面进入表签:
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
然后包裹权限控制的内容
<shiro:hasPermission name="user-delete">
<!— 有权限 —>
<button>删除用户</button>
</shiro:hasPermission>
3.代码级别权限控制:
public String deleteUser(Model model){
Subject subject = SecurityUtils.getSubject();
if(subject..checkPermission("user-delete")) {
//有权限
} else {
//无权限
}
}
4. URL拦截权限控制:
基于filter过滤器实现,我们在spring配置文件中配置shiroFilter时配置
<!--指定URL级别拦截策略 -->
<property name="filterChainDefinitions">
<value>
/css/ = anon
/js/ = anon
/images/ = anon
/validatecode.jsp = anon
/login.jsp = anon
/user/userlogin = anon
/user/deleteUser = perms["user-delete"]
/** = authc
</value>
</property>
正常情况下,没有授权会跳转到为授权(登录)页面 anon:表示不拦截(匿名用户可访问) user:使用rememberme的用户可访问 perms:对应权限可访问 role:对应的角色才能访问 authc:认证用户可访问
使用shiro进行权限控制时 这四种方法并不是进行单一的使用,是相互结合的使用从而完整的进行权限控制。
追问2:你还知道Shiro的其他组件么?简单说一下
- Subject: 与应用交互的用户
- SecurityManager: 相当于SpringMVC中的DispatcherServlet,所有具体的交互都由SecurityManager控制;它管理着所有的Subject,且负责进行认证,授权,会话和缓存的管理
- Realm: 安全实体数据源,可以有1个或多个
除了以上三个核心组件外,还包括:
- Authenticator: 认证器, 对用户身份进行验证;Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,也可以自定义
- Authorizer: 授权器,决定用户是否有权限进行某种操作,控制着用户能访问应用中的哪些功能
- SessionManager: 管理session的生命周期(可以实现单点登录)
- CacheManager: 缓存管理器
- Cryptography: 密码管理模块
面试题2:说一下Shiro认证和授权过程
认证流程
- 首先调用 Subject.login(token)进行登录,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager()设置;
- SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
- Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
- Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
- Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
shiro中有三种认证策略的具体实现: AtleastOneSuccessfulStrategy: 只要有一个realm验证成功,则成功 FirstSuccessfulStrategy: 第一个realm验证成功,则成功,后续realm将被忽略 AllSuccessfulStrategy: 所有realm成功,验证才成功
授权流程
shiro判断用户是否有权限首先会从realm中获取用户所拥有的权限角色信息,然后再匹配当前的角色或权限是否包含,从而判定用户是否有权限。
- 首先调用 Subject.isPermitted/hasRole接口,其会委托给 SecurityManager,而 SecurityManager 接着会委托给 Authorizer;
- Authorizer 是真正的授权者,如果我们调用如 isPermitted(“user:view”),其首先会通过 PermissionResolver 把字符串转换成相应的 Permission 实例;
- 在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;
- Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个 Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配如 isPermitted/hasRole 会返回 true,否则返回 false 表示授权失败。继承 AuthorizingRealm而不是实现 Realm 接口;
面试题3:Shiro中常见的异常有哪些?
异常 | 原因 |
---|---|
UnknownAccountException | 帐号不存在 |
IncorrectCredentialsException | 密码错误 |
DisabledAccountException | 帐号被禁用 |
LockedAccountException | 帐号被锁定 |
ExcessiveAttemptsException | 登录失败次数过多 |
ExpiredCredentialsException | 凭证过期 |