文章目录
安全框架
- 安全框架:对访问权限进行控制
- 安全性包括用户认证(Authentication)和用户授权(Authorization)两部分
- 用户认证:指验证某个用户是否为系统中的合法主体
- 即用户能否访问该系统
- 一般要求用户提供用户名和密码,系统通过校验用户名和密码来完成认证过程
- 用户授权:指验证某个用户是否有权限执行某个操作
- 不同用户所具有的权限是不同的
- 一般系统为不同的用户分配不同的角色,而每个角色则对应一系列的权限
SpringSecurity
Spring Security
:在架构上将认证与授权分离,并提供了扩展点- 充分利用
Spring IoC
(控制反转),DI
(依赖注入)和AOP
(面向切面编程)功能 - 为应用系统提供声明式的安全访问控制功能,减少了为系统安全控制编写大量重复代码的工作
- 确保基于
Spring
的应用程序提供身份验证和授权支持 - 与
Spring MVC
有很好地集成 ,并配备了流行的安全算法实现捆绑在一起
- 充分利用
- 对 Web 资源进行保护,最好于
Filter
;对方法调用进行保护,最好于AOP
Spring Security
在进行用户认证以及授予权限时通过各种各样的拦截器来控制权限的访问,从而实现安全
特点
Spring Security
对Web
安全性的支持大量地依赖于Servlet
过滤器- 过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理
Spring Security
提供有若干个过滤器,能够拦截Servlet
请求- 并将请求转给认证和访问决策管理器处理,从而增强安全性
- 根据需要使用适当的过滤器来保护自己的应用程序
- 要使用
Servlet
过滤器且令其正常工作必须在web.xml
文件中使用<filter>
和<filter-mapping>
元素配置- 但并不适用于使用依赖注入进行的配置
- 过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理
FilterToBeanProxy
是特殊的Servlet过滤器- 将委托转发给
Spring
应用程序上下文中的一个Bean
来完成- 被委托的
Bean
几乎和Servlet
过滤器一样- 实现
javax.servlet.Filter
接口
- 实现
- 但在
Spring
配置文件非web.xml
文件中配置
- 被委托的
FilterToBeanProxy
代理给的Bean
可以是javax.servlet.Filter
的任意实现- 可以是
Spring Security
的任何过滤器- 或自己创建的一个过滤器
- 但是
Spring Security
要求至少配置四个而且可能一打或者更多的过滤器
- 可以是
- 将委托转发给
- 除了不能脱离Spring,
shiro
的功能都有- 且
Spring Security
对Oauth
、OpenID
也有支持- Shiro 则需要手动实现
- 现租户与各个产品间单点登录已经通过 cookies 实现
- Spring Security的这两个功能可以不考虑。
- Spring Security 的权限细粒度更高
- 且
注:
- OAuth 在客户端与服务提供商之间,设置了一个授权层(authorization layer)
- 客户端不能直接登录服务提供商,只能登录授权层,将用户与客户端区分开
- 客户端登录授权层所用的令牌(token),与用户的密码不同
- 用户可在登录的时候指定授权层令牌的权限范围和有效期
- 客户端登录授权层
- 服务提供商根据令牌的权限范围和有效期向客户端开放用户储存的资料
- OpenID 系统的第一部分是身份验证
- 即通过 URI 来认证用户身份
- 目前的网站依靠用户名和密码来登录认证
- 使用 OpenID ,网站地址(URI)就是用户名
- 密码安全的存储在一个 OpenID 服务网站上
- 即通过 URI 来认证用户身份
配置
-
添加相应依赖信息:包括 Thymeleaf 整合 security 依赖
-
<!-- 其他需要依赖信息略:包括数据库依赖 ---> <!-- security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- 模板支持安全框架 --> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> </dependency>
-
- 导入 Spring Security 依赖
- 创建配置类继承
WebSecurityConfigurerAdapter
- 重写方法完成认证与授权
WebSecurityConfigurerAdapter
:自定义 Security 策略AuthenticationManagerBuilder
:自定义认证策略@EnableWebSecurity
:开启 WebSecurity 模式
- 在 页面中添加相应请求或方法验证权限
请求拦截
配置类
- 配置需要成对出现,并且配置的顺序也很重要
- 声明在前面的规则拥有更高的优先级
// AOP 实现
@EnableWebSecurity
public class SecurityConf extends WebSecurityConfigurerAdapter {
// 请求授权
@Override
protected void configure(HttpSecurity http) throws Exception {
// 链式编写:不同模块使用 and() 连接,也可以分开写
// 对请求授权
http.authorizeRequests()
// 不需要通过登录验证就可以被访问的资源路径
.antMatchers("/", "/index", "/toLogin").permitAll()
// 需要对应权限、角色访问
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3")
// 上面的任何请求都必须经过身份验证
.anyRequest().authenticated();
// 表单登录,用户未登录时,访问任何资源都转跳到该路径,即登录页面
http.formLogin()
// 登录页面,设置为自定义的登录页面
.loginPage("/toLogin")
// 登录表单中 action 的地址,即实际处理认证请求的路径
.loginProcessingUrl("/login")
// 获取用户名输入框参数:默认 username
.usernameParameter("username")
// 获取密码输入框参数:默认 password
.passwordParameter("password")
// 登录认证成功后默认转跳的路径,登陆失败后返回原页面
.successForwardUrl("/").failureForwardUrl("/toLogin")
.permitAll()
// 注销
http.logout()
// 登出成功后返回的请求
.logoutSuccessUrl("/")
.permitAll()
// 关闭 csrf 功能:跨站请求伪造,默认只能通过 post 方式提交请求
http.csrf().disable()
// 记住我功能,使用 Cookie 缓存,默认有效期14天
.rememberMe().rememberMeParameter("remember");
}
}
?
:匹配任何单字符*
:匹配 0 或者任意数量的字符**
:匹配 0 或更多目录
常用方法
permitAll()
:允许任何访问denyAll()
:拒绝所有访问anonymous()
:允许匿名用户访问authenticated()
:允许认证的用户进行访问hasRole(String)
:- 用户具备给定角色、用户组 允许访问
hasAnyRole(String…)
:- 用户具有给定角色、用户组 中的一个允许访问
hasAuthority(String)
:- 用户具备给定权限允许访问
hasAnyAuthority(String…)
:- 用户具备给定权限中的某一个允许访问
principal
:- 允许直接访问代表当前用户的主体对象
rememberMe()
:- 用户是通过
Remember-me
功能认证允许访问
- 用户是通过
fullyAuthenticated()
:- 如果用户是完整认证允许访问
- 不是通过Remember-me功能认证的
hasIpAddress(String)
:- 请求来自给定ip地址允许访问
isAnonymous()
:当前委托人是否为匿名用户isRememberMe()
:当前主体是否是“记住我”的用户not()
:对其他访问结果求反
权限认证
- 权限认证和授权在同一配置类中
内存认证
AuthenticationManagerBuilder
:使用构造者方式来构建- 先调用
inMemoryAuthentication()
- 指定用户存储在内存中
- 调用了
passwordEncoder()
- 指定认证密码的加密方式
- 在
Spring security5
后,必须指定加密方式,不然程序会报错
- 调用的
withUser()、password()、authorities()
- 指定用户的账号、密码以及权限名
- 添加完一个用户后,使用
and()
方法来连接下一个用户的添加
- 先调用
- 这种配置在修改用户时必须修改代码
- 甚至无法注册
@EnableWebSecurity
public class SecurityConf extends WebSecurityConfigurerAdapter {
// 权限认证
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// 在内存里面存储用户的身份认证和授权信息
auth.inMemoryAuthentication()
// 添加用户权限:用户名、密码(加密)、角色信息
.withUser("user").password(passwordEncoder().encode("123456")).roles("vip1")
// 添加多个用户使用 and()
.and()
.withUser("admin").password(passwordEncoder().encode("123456")).roles("vip1", "vip2", "vip3")
// 配置BCrypt加密
.and().passwordEncoder(passwordEncoder());
}
// 加密对象,密码需要加密才能安全保存
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
JDBC 认证
- 将用户信息存储在数据库中
- 方便对用户信息进行增删改查
- 还可以添加除认证信息外的附加信息
- 调用
jdbcAuthentication()
- 指定使用
jdbc
的方式来查询用户和权限
- 指定使用
dataSource()
:指定数据库连接信息passwordEncoder()
:指定密码加密规则- 用户的密码数据应该以同样的方式进行加密存储
- 否则两个加密方式不同无法匹配
- 用户的密码数据应该以同样的方式进行加密存储
usersByUsernameQuery()
、authoritiesByUsernameQuery()
- 定义了查询用户和权限信息的 sql 语句
Spring security
默认了查询用户、权限甚至还有群组用户授权的sql- 三条默认的 sql 存放在
org......userdetails.jdbc.JdbcDaoImpl
中 - 使用默认的,那表中关键性的字段必须和语句中的一致
- 三条默认的 sql 存放在
// 自动装配数据源对象
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 使用 JDBC 存储身份认证信息
auth.jdbcAuthentication()
// 传入数据源
.dataSource(dataSource)
// 指定密码加密方式
.passwordEncoder(passwordEncoder())
// 根据用户名获取用户信息
.usersByUsernameQuery("select username, password, status from Users where username = ?")
// 根据用户名获取认证权限
.authoritiesByUsernameQuery("select username, authority from Authority where username = ?");
}
//定义加密对象
@Bean
private PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
LDAP用户存储
LDAP
:轻型目录访问协议,一个开放的,中立的,工业标准的应用协议- 通过IP协议提供访问控制和维护分布式信息的目录信息
- 即:将用户信息存放在另外一台服务器中
userSearchFilter()
、groupSearchFilter()
- 设置用户和群组的过滤条件
userSearchBase()
、groupSearchBase()
- 设置了搜索起始位置
contextSource().url()
:- 设置
LDAP
服务器的地址
- 设置
- 没有远程的服务器可以使用
contextSource().root()
使用嵌入式LDAP
服务器- 将使用项目中的用户数据文件来提供认证服务
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> configurer = auth.ldapAuthentication()
.userSearchBase("ou=people")
.userSearchFilter("(uid={0})")
.groupSearchBase("ou=groups")
.groupSearchFilter("member={0}");
configurer.passwordCompare()
.passwordEncoder(passwordEncoder())
.passwordAttribute("passcode");
configurer.contextSource().url("ldap://xxxxx.com:33389/dc=xxxxxx,dc=com");
}
private PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
MyBatis
自定义认证方式:不仅限于 MyBatis 使用
- 自行使用认证名称来查找对应的用户数据
- 然后交给 Spring Security 使用
- 需要定义一个实现
UserDetailsService
的service
类- 得到用户对象
UserServce
- 只需要实现一个方法:
loadUserByUsername()
- 使用传过来的
username
来匹配带有密码等信息的用户实体User
类需要实现UserDetails
- 这是 Security 中的 user 类,而非自定义 User 类
- 即:查到的信息里必须得有
Spring Security
所需要的信息
- 使用传过来的
- 类中可以注入 mapper 等等查用户
- 如果查不到,留在这个页面
- 如果查到了,做出一定逻辑(例如判空等等)
- 把用户信息封装到
Spring Security
的User
类中- 区分自定义自定义
User
和Spring Security
的User
- 区分自定义自定义
Spring Security
拿前台的数据比较,做出操作- 例如:认证成功或者错误
- 把用户信息封装到
@Service
public class UserService implements UserDetailsService {
@Qualifier("userDao")
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username == null){
throw new UsernameNotFoundException("用户名不存在");
}
// 获取用户对象
security.pojo.User user = userDao.getUserByName(username);
List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
// 得到用户权限
String role = user.getRoles();
// 权限不为空放到到 List 集合中
if (role != null && !"".equals(role)){
authorityList.add(new SimpleGrantedAuthority(role.trim()));
}
// 封装到框架的 USer 类中并提交
return new User(user.getUsername(),user.getPassword(),authorityList);
}
}
SecurityConf
- 指定
Spring Security
自定义的UserDetailsService
实现类- 会自动去调用
loadUserByUsername()
来查找用户 - 用户的密码和提交密码加密方式要相同
- 否则两个加密方式不同无法匹配
- 会自动去调用
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
//密码加密方式和数据库密码加密方式要相同
.passwordEncoder(passwordEncoder());
}
@Bean
private PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
表单登录
<!-- action 请求和 loginProcessingUrl 保持一致-->
<form th:action="@{/login}" method="post">
<!-- 用户名输入框 name 属性对应 usernameParameter() -->
<input type="text" placeholder="Username" name="username">
<!-- 密码输入框 name 属性对应 passwordParameter() -->
<input type="password" placeholder="PassWord" name="password">
<!-- 保存账号按钮 name 属性对应 rememberMeParameter() -->
<input type="checkbox" class="ui checkbox bordered" name="remember">保存账号密码
<input type="submit" value="提交"/>
</form>
验证状态权限
-
整合 Thymeleaf 模板使用
-
命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<!-- 判读未登录时进行操作 -->
<div sec:authorize="!isAuthenticated()"/>
<!-- 判断已登录时进行操作 -->
<div sec:authorize="isAuthenticated()"/>
<!-- 当前用户有其中任一角色时进行操作 -->
<div sec:authorize="hasAnyRole('vip1','vip2','vip3')"/>
<!-- 当前用户有这个角色时进行操作 -->
<div sec:authorize="hasRole('vip1')"/>
<!-- 显示当前账号所有角色 -->
<span sec:authentication="principal.authorities">
shiro
Apache Shiro
:一个强大且易用的 Java 安全框架- 能够非常清晰的处理身份验证、授权、管理会话以及密码加密
- 利用其易于理解的 API,可以快速、轻松地获得任何应用程序
- 从最小的移动应用程序到最大的网络和企业应用程序
- Shiro:主要分为两个部分:认证和授权
- Shiro 只是一个框架而已,内容需要自己去构建
- 前后是自己的,中间是 Shiro 去搭建和配置好的
核心组件
- 三个核心组件:
Subject
、SecurityManager
和Realms
Subject
:当前操作用户- 在
Shiro
中,Subject
并不仅仅指人- 也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物
- 仅仅意味着当前跟软件交互的东西
Subject
代表当前用户的安全操作,SecurityManager
则管理所有用户的安全操作
- 在
SecurityManager
:Shiro
框架的核心,典型的Facade
模式- 通过
SecurityManager
来管理内部组件实例,并提供安全管理的各种服务
- 通过
Realm
:充当Shiro
与应用安全数据间的 桥梁 或 连接器- 当对用户执行认证(登录)和授权(访问控制)验证
Shiro
从应用配置的Realm
中查找用户及其权限信息Realm
实质上是安全相关的DAO
- 封装了数据源的连接细节,并在需要时将相关数据提供给
Shiro
- 封装了数据源的连接细节,并在需要时将相关数据提供给
- 当配置
Shiro
时,必须至少指定一个Realm
,用于认证和(或)授权- 配置多个
Realm
是可以的,但至少需要一个
- 配置多个
Shiro
内置了可连接大量安全数据源(又名目录)的Realm
- 如
LDAP
、关系数据库(JDBC
)、类似INI
的文本配置资源以及属性文件等 - 若缺省的
Realm
不能满足需求,可以插入代表自定义数据源的Realm
实现
- 如
组件
UsernamePasswordToken
:Shiro
用来封装用户登录信息,使用用户的登录信息创建令牌Token
- 登录的过程即
Shiro
验证令牌是否具有合法身份以及相关权限
AuthenticationInfo
:用户的角色信息集合,认证时使用AuthorizationInfo
,角色的权限信息集合,授权时使用DefaultWebSecurityManager
:安全管理器- 自定义的
Realm
需要注入到DefaultWebSecurityManager
进行管理才能生效
- 自定义的
ShiroFilterFactoryBean
:过滤器工厂Shiro
的基本运行机制是开发者定制规则,Shiro 去执行- 即
ShiroFilterFactoryBean
创建Filter
对象来完成
功能模块
Session Manager
:会话管理,用户登录后的session相关管理Cryptography
:加密,密码加密等Web Support
:Web支持,集成Web环境Caching
:缓存,用户信息、角色、权限等缓存到如redis等缓存中Concurrency
:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去Testing
:测试支持Run As
:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问Remember Me
:记住我
特点
- 易于理解的
Java Security API
- 简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等)
- 对角色的简单的签权(访问控制),支持细粒度的签权
- 支持一级缓存,以提升应用程序的性能
- 内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境
- 异构客户端会话访问
- 非常简单的加密 API
- 不跟任何的框架或者容器捆绑,可以独立运行
配置
- 添加依赖信息:包括模板支持、shiro、日志 以及数据库等其他依赖
<!-- thymeleaf 整合 shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.8.0</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</dependency>
MyController
//仅有部分相关方法
// 要登录请求处理:跳转到登录页面
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
// 登录处理
@RequestMapping("/login")
public String login(String username, String password, Model model) {
// 获取当前用户 subject 对象
Subject subject = SecurityUtils.getSubject();
// 使用传进来的用户名、密码创建令牌
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
// 执行登录操作:由框架进行处理
// 认证、授权在 realm 中
// 拦截规则在过滤器中定义
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
// 拦截异常情况
model.addAttribute("message", "用户名不存在");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("message", "密码错误");
return "login";
}
}
// 认证异常请求处理
@RequestMapping("/unauthorized")
@ResponseBody
public String unauthorized(){
return "未获得权限,无法访问";
}
Realm
public class UserRealm extends AuthorizingRealm {
// 注入对象从数据库中查找用户信息
@Autowired
private UserDao userDao;
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 得到认证时传入的用户对象(或用户名、id 等对象)
User user = (User)principalCollection.getPrimaryPrincipal();
// 或得到 Subject 当前用户对象
// Subject subject = SecurityUtils.getSubject();
// 转为自定义的 User 对象
// User user = (User)subject.getPrincipal();
// 硬授权:所有进入的用户都会授权 user:add
// info.addStringPermission("user:add");
// 获取用户角色信息并添加角色(或使用授权)
info.addRole(user.getRoles());
return info;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 得到传进来的用户名
String username = (String) authenticationToken.getPrincipal();
// 通过用户名查询用户
User user = userDao.getByName(username);
// 如果用户为 null
if (user == null) {
// 不正确时直接返回 null,会自动捕捉异常
return null;
}
// 传入用户对象、密码、自定义 Realm 类名传进去由框架自动匹配
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
/*
第一个参数可以是用户对象、用户名、用户id 等
可以在授权时获取此时传入的信息,密码会由框架自动匹配
*/
}
}
ShiroConfig
- 将
CustomRealm
和SecurityManager
等注入到spring
容器 - 自动装配 3 个 bean 实例
- 自定义过滤器
MyRealm
- 业务逻辑全部定义在这个 bean 中
DefaultWebSecurityManager
- 将
MyRealm
注入到DefaultWebSecurityManager
完成注册
- 将
ShiroFilterFactoryBean
- Shiro 自带的一个
Filter
工厂实例 - 所有的认证和授权判断都是由这个 bean 生成的 Filter 对象来完成的
- Shiro 自带的一个
- 自定义过滤器
- Shiro 框架的运行机制
- 开发者只需要定义规则,进行配置
- 具体的执行者全部由 Shiro 自己创建的 Filter 来完成
@Configuration
public class ShiroCong {
// Realm 对象,将自定义验证方式加入容器
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
// DefaultWebSecurityManager: 安全管理器
// 权限管理,配置主要是 Realm 的管理认证
@Bean
public DefaultWebSecurityManager securityManager(){
// 关联 Realm 对象
return new DefaultWebSecurityManager(userRealm());
}
// ShiroFilterFactoryBean:过滤器对象
// Filter 工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 关联 SecurityManager 对象
bean.setSecurityManager(securityManager());
// 使用 LinkHashMap 存放路径拦截规则,有序:先配置先生效
Map<String, String> filterMap = new LinkedHashMap<>();
// 默认无法配置多角色访问同一权限,需要自定义修改 shiro 过滤器配置
// 授权: 有 add 角色才能访问 add 请求
filterMap.put("/user/add", "roles[add]");
// 授权:必须有 user:update 权限才能访问 update
filterMap.put("/user/update", "perms[user:update]");
// 拦截: 对 user下资源必须登陆后才能访问
filterMap.put("/user/**", "authc");
// 登出
filterMap.put("/logout", "logout");
// 对所有用户认证必须登录后访问
filterMap.put("/**", "authc");
// 将设置了请求权限的 Map 集合放到 过滤器对象中
bean.setFilterChainDefinitionMap(filterMap);
// 未登录跳转到登录页
bean.setLoginUrl("/toLogin");
// 登陆成功请求;跳转首页
bean.setSuccessUrl("/index");
// 认证异常请求处理:权限不足
bean.setUnauthorizedUrl("/unauthorized");
return bean;
}
// 配置 ShiroDialect 方言标签整合 Thymeleaf
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
Shiro 过滤器
自定义 Shiro 过滤器
- 对 URL 进行拦截
- 没有认证的需要认证
- 认证成功的则可以根据需要判断角色及权限
- 过滤器需要自定义
- 然后指定认证和授权的逻辑
- 继承抽象类
AuthorizingRealm
,实现两个抽象方法分别完成授权和认证的逻辑
过滤器分类
- 认证过滤器
anon
:无需认证即可访问,游客身份authc
:必须认证、登录 才能访问authcBasic
:需要通过 httpBasic 认证user
:不一定已通过认证- 只要曾经被 Shiro 记录登录状态的用户就可以正常发起请求
- 比如
rememberMe
- 授权过滤器
perms
:必须拥有对某个资源的访问权限(授权)才能访问role
:必须拥有某个角色权限才能访问port
:请求的端口必须为指定值才可以访问rest
:请求必须是 RESTful- method 为 post、get、delete、put
ssl
:必须是安全的 URL 请求,协议为 HTTPS
常见shiro异常
AuthenticationException
:认证异常Shiro
在登录认证过程中,认证失败需要抛出的异常- 包含以下子类
CredentitalsException
:凭证异常IncorrectCredentialsException
:不正确的凭证ExpiredCredentialsException
:凭证过期
AccountException
:账号异常ConcurrentAccessException
:并发访问异常,多个用户同时登录时抛出UnknownAccountException
:未知的账号ExcessiveAttemptsException
:认证次数超过限制DisabledAccountException
: 禁用的账号LockedAccountException
:账号被锁定UnsupportedTokenException
:使用了不支持的Token
AuthorizationException
: 授权异常Shiro
在登录认证过程中,授权失败需要抛出的异常- 包含以下子类
UnauthorizedException
- 抛出以指示请求的操作或对请求的资源的访问是不允许的
UnanthenticatedException
- 当尚未完成成功认证时,尝试执行授权操作时引发异常
页面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="Thymeleaf"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
<h3>index</h3>
<!-- 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。 -->
<p shiro:guest="">Please <a href="login.html">login</a></p>
<!-- 认证通过或已记住的用户。 -->
<p shiro:user="">
Welcome back John! Not John? Click <a href="login.html">here</a> to login.
</p>
<!-- 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。 -->
<p shiro:authenticated="">
Hello, <span shiro:principal=""></span>, how are you today?
</p>
<a shiro:authenticated="" href="updateAccount.html">Update your contact information</a>
<!-- 输出当前用户信息,通常为登录帐号信息。 -->
<p>Hello, <shiro:principal/>, how are you today?</p>
<!-- 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。 -->
<p shiro:notAuthenticated="">
Please <a href="login.html">login</a> in order to update your credit card information.
</p>
<!-- 验证当前用户是否属于该角色。 -->
<a shiro:hasRole="admin" href="admin.html">Administer the system</a><!-- 拥有该角色 -->
<!-- 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。 -->
<p shiro:lacksRole="developer"><!-- 没有该角色 -->
Sorry, you are not allowed to developer the system.
</p>
<!-- 验证当前用户是否属于以下所有角色。 -->
<p shiro:hasAllRoles="developer, 2"><!-- 角色与判断 -->
You are a developer and a admin.
</p>
<!-- 验证当前用户是否属于以下任意一个角色。 -->
<p shiro:hasAnyRoles="admin, vip, developer,1"><!-- 角色或判断 -->
You are a admin, vip, or developer.
</p>
<!--验证当前用户是否拥有指定权限。 -->
<a shiro:hasPermission="userInfo:add" href="createUser.html">添加用户</a><!-- 拥有权限 -->
<!-- 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。 -->
<p shiro:lacksPermission="userInfo:del"><!-- 没有权限 -->
Sorry, you are not allowed to delete user accounts.
</p>
<!-- 验证当前用户是否拥有以下所有角色。 -->
<p shiro:hasAllPermissions="userInfo:view, userInfo:add"><!-- 权限与判断 -->
You can see or add users.
</p>
<!-- 验证当前用户是否拥有以下任意一个权限。 -->
<p shiro:hasAnyPermissions="userInfo:view, userInfo:del"><!-- 权限或判断 -->
You can see or delete users.
</p>
<a shiro:hasPermission="pp" href="createUser.html">Create a new User</a>
</body>
</html>