Shiro认证(Authentication)
目录
1.1 Shiro简介
Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、会话管理和加密等功能。对于任意一个应用程序,Shiro都可以提供全面的安全管理服务,对比Spring Security,可能没有Spring Security功能强大,但是完美在实际工作中可能不需要那么复杂的功能,所以使用简单易用的Shiro就已经足够了。
1.1.1 Shiro特性
Shiro的特点
- 易于理解的API 简单的身份认证,支持多种数据源
- 简单的授权和鉴权
- 简单的加密API
- 支持缓存,以提升应用程序的性能
- 内置会话管理,适用于Web以及非Web的环境
- 不跟任何的框架或者容器捆绑,可以独立运行
Authentication(认证),Authorization(授权),Session Management(会话管理),Cryptography(加密)被Shiro框架的开发团队称之为应用安全的四大基石
Authentication:身份认证/登录。
-
Authorization:权限验证,即判断用户是否能在系统中做某件 事情。
-
Session Management:会话管理,用户登录后就是一次会 话,在没有退出之前,它的所有信息都在会话中,会话可以是 JavaSE环境的,也可以是Web环境的。
-
Cryptography:加密,保护数据的安全性。即密码加密存储到 数据库,而不是明文存储。 Web Support:Web 支持,可以非常容易的集成到Web环境。
-
Caching:缓存。在用户登录后,用户信息、拥有的权限不必每 次去查,这样可以提高效率。
-
Concurrency:Shiro支持多线程应用的并发验证,即如在一个 线程中开启另一个线程,能把权限自动传播过去。
-
Testing:提供测试支持。
-
Run As:允许一个用户假装为另一个用户的身份进行访问。
-
Remember Me:记住我,即一次登录后,下次再来就不用登 录了。
1.1.2 Shiro架构
从应用程序的角度来看如何使用Shir完成工作。
如下图:
可以看到应用代码直接交互的对象是Shuject,也就是说Shiro的对外API核心就是Subject;其每个API的含义如下:
-
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都能是Subject,如网络爬虫,机器人等,即一个抽象概念。所有Subject都绑定到SecurityManager,与Subject的所有交互都是委托给SecurityManager。
-
SecurityManager:安全管理器,即所有与安全有关的操作都会与SecurityManager交互,且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互。
-
Realm:域,Shiro从Realm对象获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证是否能进行操作;可以把Realm看成DataSource,即安全数据源。
接下来完美从Siro内部来看下Shiro的架构。
如下图:
-
Subject:主体。 Subject 在Shiro中是一个接口,接口中定义了认证授权的相关 方法。程序通过调用 Subject 的方法进行认证授权,而 Subject 使用 SecurityManager 进行认证授权
-
SecurityManager:权限管理器,它是Shiro的核心。通过 SecurityManager 可以完成具体的 认证、授权等操作, SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理。 SecurityManager 是 一个接口,继承了 Authenticator , Authorizer , SessionManager 三个接口。
-
Authenticator:认证器。对用户登录时进行身份认证
-
Authorizer:授权器。用户认证通过后,在访问功能时需要通过授权器判断用户 是否有此功能的操作权限。
-
SessionManager:会话管理。shiro框架定义了一套会话管理,它不依赖web容器的 session,所以shiro可以使用在非web应用上。
-
Realm:领域。他是连接数据源+认证功能+授权功能的具体实现。 SecurityManager 通过 Realm 获取用户的身份和权限信息,并对用户进行认证和授权。
-
SessionDAO:会话dao,是对会话进行操作的一套接口。它可以将session数据存 储到数据库或缓存服务器中。
-
CacheManager:缓存管理,将用户权限数据存储在缓存中,这样可以减少权限查询 次数,提高性能。
-
Cryptography:密码管理,Shiro提供了一套加密/解密的组件,方便开发。
1.2 认证(Authentication)
在Shiro中,认证指的是识别和证明操作者是一个合法用户。
1.2.1 快速上手
添加依赖
<!-- 引入shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>2.0.1</version>
</dependency>
<!-- 引入Java-API-->
<!-- 因为Spring Boot3.3.4版本更新的原因原有的API的位置改变了所以需要引入Java-API,原来是不用引入的 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
配置shiro.ini
在resoureces目录下新建shiro.ini文件配置用户认证数据
[users]
#用户账号和密码
#配置规则:用户账号=密码
admin = 123456
czkt = 111111
认证测试
public class ShiroTester {
@Test
public void testShiro() {
// 通过shiro.ini配置文件创建realm
IniRealm realm = new IniRealm("classpath:shiro.ini");
// 配置SerurityManager
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 注入创建的realm
securityManager.setRealm(realm);
SecurityUtils.setSecurityManager(securityManager);
// 操作Subject,进行认证
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
try{
subject.login(token);
}catch (Exception e) {
System.out.println("认证异常:");
e.printStackTrace();
}
System.out.println("是否认证成功:" + subject.isAuthenticated());
System.out.println("身份信息:" + subject.getPrincipal());
}
}
运行结果
是否认证成功:true
身份信息:admin
1.2.2 认证流程
通过前面的测试用例,我们只看到如何在程序代码中验证一个Subject,现在我们看一下当一个验证发生是Shiro内部发生了什么。
流程如下:
- 首先调用 Subject.login(Token) 进行登录,其会自动委托给 Security Manager,调用之前通过 SecurityUtils.setSecurityManager()设置Security Manager;
- SecurityManager 负责真正的身份验证逻辑,他会委托给 Authenticator 进行身份验证;
- Authentication 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现。
- Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证。
- Authenticator 会把相应的Token 传入 Realm,从Realm获取身份信息,如果没有返回 / 抛出异常表示身份验证失败了,此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
1.3 SpringBoot+Shiro认证
SpringBoot+Shiro实现动态认证,Shiro只要做两件事即可:1、自定义Realm;2、配置Shiro相关对象
1.3.1 基础代码调整
UserMapper新增findUserByUsrName方法
User findUserByUsrName(String usrName);
根据用户名查询用户对象,密码(凭证)是否正确由后续Shiro在认证时判断。
UserService接口新增findUserByUsrName方法和在UserServiceImpl实现类中实现
1.3.2 自定义Reaml
public class MyShiroRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) throws AuthenticationException{
System.out.println("调用MyShiroRealm.doGetAuthorizationInfo获取权限信息!");
User user = (User) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("调用MyShiroRealm.doGetAuthenticationInfo获取身份信息!");
// 获得身份信息
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String usrName = token.getUsername();
User user = userService.findUserByUsrName(usrName);
if(user == null){
throw new UnknownAccountException(); // 账号错误
}
if(user.getUsrFlag() == null || user.getUsrFlag().intValue() == 0){
throw new LockedAccountException(); // 账号被锁定
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getUsrPassword(),getName());
return info;
}
}
1.3.3 配置Shiro相关对象
@Configuration
public class ShiroConfig {
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm shiroRealm = new MyShiroRealm();
return shiroRealm;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
// 在注册bean的时候,加入下面这句话,将securityManager放到SecurityUtils里面
SecurityUtils.setSecurityManager(securityManager);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
shiroFilterFactory.setSecurityManager(securityManager);
return shiroFilterFactory;
}
}
1.3.4 Controller层重写登陆方法
@PostMapping("/doLogin")
public String doLogin(String usrName, String usrPassword, Model model, HttpSession session) {
try {
UsernamePasswordToken token = new UsernamePasswordToken(usrName, usrPassword);
Subject subject = SecurityUtils.getSubject();
subject.login(token); // 认证、登录
User user = (User) subject.getPrincipal();
session.setAttribute("loginUser", user);
return "redirect:/main";
}catch (UnknownAccountException | IncorrectCredentialsException e) {
model.addAttribute("msg", "用户名或密码错误,登录失败!");
return "login";
}catch (LockedAccountException e) {
model.addAttribute("msg", "账号被锁定,登录失败!");
return "login";
}catch (AuthenticationException e) {
model.addAttribute("msg", "登录失败!");
return "login";
}
}
@GetMapping("/logout")
public String logOut(HttpSession session) {
session.removeAttribute("loginUser");
session.invalidate();
return "/login";
}
原文地址:https://blog.csdn.net/Bisikl/article/details/142957157
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!