Apache Shiro是一个强大灵活的开源安全框架,提供认证、授权、会话管理以及密码加密等功能。
- Shiro官网:shiro.apache.org/
- 中文帮助文档:greycode.github.io/shiro/doc/r…
使用 Apache Shiro 可以做到:
-
认证: 或“登录”,用以验证用户身份。
-
授权: 访问控制, 比如决定谁可以访问某些资源。
-
会话管理: 管理用户相关的session,即使是在非web或EJB应用中。
-
加密: 可以非常方便地使用(各种)加密算法保证数据的安全。
Authenticator(用户认证)
Authenticator是用于执行用户的认证行为的组件,验证用户是否拥有对应的身份
Authorizer (用户授权)
Authorizer用于控制程序中用户的访问,最终决定用户是否被允许做什么。即判断用户可以进行什么操作。比如可以判断用户是否拥有某个角色,判断用户是否对某个资源拥有对应的权限。
SessionManager (会话管理)
SessionManager可以创建用户会话(Session)维护其生命周期并在所有环境中提供了强大的用户体验。用户再进行登录后就是一次会话,在用户没有退出去之前,所有的信息都在这个会话中。
Cryptography (加密)
加密是企业级安全框架固有的功能,Shiro的crypto包对密码、哈希和各种编码实现提供了易于使用和理解的封装 。
Shiro的架构主要有三个顶级概念:Subject, SecurityManager和Realms。图中展示了这些组件间的交互,接下来详细介绍:
-
Subject:
Subject就是“当前执行用户”。任何可以和应用交互的用户都可以是Subject。Subject的实例都会(也是必须)绑定一个SecurityManager,对Subject的操作会转为Subject与SecurityManager之间的交互。 -
SecurityManager:
SecurityManager是Shiro架构的核心,管理所有的Subject。 当使用Subject进行各种操作时,执行操作的就是SecurityManager,这一点在上面的图中有所体现。 -
Realms:Realm在shiro与应用安全数据之间扮演“桥梁”/“连接器”的角色。每当因为执行认证或授权操作而需要像用户帐号等安全相关的数据时,shiro会从程序配置的一个或多个Realm中查询。你可以根据需要配置多个
Realm。
SpringBoot与Shiro整合
- 导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
- 定义MyRealm
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//自定义登录认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证方法");
//1. 获取用户身份信息
String name = authenticationToken.getPrincipal().toString();
//2.调用业务层获取用户信息(数据库)
User userInfo = userService.getUserInfo(name);
if (userInfo != null){
return new SimpleAuthenticationInfo(name,userInfo.getPwd(),authenticationToken.getPrincipal().toString());
}
return null;
}
}
- 定义ShiroConfig
@Configuration
public class shiroConfig {
@Autowired
private MyRealm myRealm;
//配置SecurityManager
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm);
return manager;
}
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager manager){
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(manager);
//设置过滤器
Map<String,String> map = new LinkedHashMap<>();
map.put("/user/toLogin","anon");
map.put("/user/login","anon");
map.put("/**","authc");
filterFactoryBean.setLoginUrl("/user/toLogin");
filterFactoryBean.setFilterChainDefinitionMap(map);
return filterFactoryBean;
}
}
- 定义Controller
@Controller
@RequestMapping("user")
public class UserController {
@GetMapping("toLogin")
public String tologin(){
System.out.println("返回登陆界面");
return "login";
}
@GetMapping("login")
public String userLogin(String name, String pwd){
//1. 获取subject对象
Subject subject = SecurityUtils.getSubject();
//2.封装请求数据到token
AuthenticationToken token = new UsernamePasswordToken(name,pwd);
//3. 调用login方法进行登录认证
try {
//执行登录认证的方法
subject.login(token);
return "index";
} catch (AuthenticationException e) {
e.printStackTrace();
return "login";
}
}
}
- login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证</h1>
<form action="/user/login">
<div>用户名:<input type="text" name="name"></div>
<div>密码:<input type="password" name="pwd"></div>
<div><input type="submit"></div>
</form>
</body>
</html>