什么是Shiro
是一款主流的Java安全框架,不依赖任何容器,可以运行在Java SE和Java EE项目中,它的主要作用是对访问系
统的用户进行身份认证、授权、会话管理、加密等操作。
Shiro就是用来解决安全管理的系统化框架。
用户、角色、权限
会给角色赋予权限,给用户赋予角色
1、UsernamePasswordToken, Shiro 用来封装用户登录信息,使用用户的登录信息来创建令牌Token。
2、SecurityManager, Shiro 的核心部分,负责安全认证和授权。
3、Suject, Shiro 的一个抽象概念,包含了用户信息。
4、Realm,开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在Realm中。
5、AuthenticationInfo, 用户的角色信息集合,认证时使用。
6、AuthorzationInfo, 角色的权限信息集合,授权时使用。
7、DefaultWebSecurityDManager, 安全管理器,开发者自定义的Realm需要注入到
DefaultWebSecurityDManager进行管理才能生效。
8、ShiroFilterFactoryBean, 过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,具体的执行操
作就是由ShiroFilterFactoryBean创建的一个个Filter对象来完成。
Shiro的运行机制如下图所示。
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如爬虫、机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者。
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是shiro的核心, SecurityManager相当于spring mvc中的dispatcherServlet前端控制器。
Realm:域,shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
一、数据库设计
二、创建项目导入依赖(springboot项目为例)
1、用idea新建Spring Initializr项目,项目结构如下:
2、添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2.配置application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/shiro?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&allowPublicKeyRetrieval=true
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private Integer id;
private String username;
private String password;
private String perms;
private String role;
}
public class AccountRealm extends AuthorizingRealm {
@Autowired
private AccountService accountService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取当前登录的用户信息
Subject subject = SecurityUtils.getSubject();
Account account = (Account) subject.getPrincipal();
//设置角色
Set<String> roles = new HashSet<>();
roles.add(account.getRole());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//设置权限
info.addStringPermission(account.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
Account account = accountService.findByUsername(token.getUsername());
if (account != null){
return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
}
return null;
}
}
ShiroFilterFactoryBean:是个拦截器,在请求进入控制层前将其拦截,需要将安全管理器SecurityManager注入其中。
SecurityManager:安全管理器,需要将自定义realm注入其中,以后还可以将缓存、remeberme等注入其中
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
//权限设置
Map<String,String> map = new Hashtable<>();
map.put("/main","authc");
map.put("/manage","perms[manage]");
map.put("/administrator","roles[administrator]");
factoryBean.setFilterChainDefinitionMap(map);
//设置登录页面
factoryBean.setLoginUrl("/login");
//设置未授权页面
factoryBean.setUnauthorizedUrl("/unauth");
return factoryBean;
}
@Bean
public DefaultWebSecurityManager securityManager(@Qualifier("accountRealm") AccountRealm accountRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(accountRealm);
return manager;
}
@Bean
public AccountRealm accountRealm(){
return new AccountRealm();
}
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
登录过程其实只是处理异常的相关信息,具体的登录验证交给shiro来处理
@Controller
@Slf4j
public class AccountController {
// 请打开麦克风交流
@GetMapping("/{url}")
public String redirect(@PathVariable("url") String url){
return url;
}
@PostMapping("/login")
public String login(String username , String password, Model model){
log.warn("username is ======>"+username);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try {
subject.login(token);
Account account = (Account) subject.getPrincipal();
subject.getSession().setAttribute("account",account);
return "index";
} catch (UnknownAccountException e) {
e.printStackTrace();
model.addAttribute("msg","用户名错误!");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误!");
e.printStackTrace();
return "login";
}
}
@GetMapping("/unauth")
@ResponseBody
public String unauth(){
return "未授权,无法访问:";
}
@GetMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
}
登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="shortcut icon" href="#"/>
</head>
<body>
<form action="/login" method="post">
<table>
<span th:text="${msg}" style="color: red"></span>
<tr>
<td>用户名:</td>
<td>
<input type="text" name="username"/>
</td>
</tr>
<tr>
<td>密码:</td>
<td>
<input type="password" name="password"/>
</td>
</tr>
<tr>
<td>
<input type="submit" value="登录"/>
</td>
</tr>
</table>
</form>
</body>
</html>
index页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="shortcut icon" href="#"/>
</head>
<body>
<h1>index</h1>
<div th:if="${session.account != null}">
<span th:text="${session.account.username}+'欢迎回来!'"></span><a href="/logout">退出</a>
</div>
<a href="/main">main</a> | <a href="manage">manage</a> | <a href="/administrator">administrator</a>
<!--<a href="/main">main</a> <br/>-->
<!--<div shiro:hasPermission="manage">-->
<!--<a href="manage">manage</a> <br/>-->
<!--</div>-->
<!--<div shiro:hasRole="administrator">-->
<!--<a href="/administrator">administrator</a>-->
<!--</div>-->
</body>
</html>
administator.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>administator</h1>
</body>
</html>
main.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>main</h1>
</body>
</html>
manage.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>manage</h1>
</body>
</html>
认证过滤器
auths:必须认证
authcBasic:需耍通过HTTPBasic认证
user:不-定通过认证,只要曾经被Shiro记录即可,比如:记住我
授权过滤器
perms:必须拥有某个权限才能访问
role:必须拥有某个角色才能访问
port:请求的端口必须是指定值才可以
rest:请求必须基于RESTful, POST. PUT. GET. DELETE
ssl:必须是安全的URL请求,协议HTTPS
创建3个页面,main.html. manage.html. administrator.html
访问权限如下:
1、必须登录才能访问main.html
2、当前用户必须拥有manage授权才能访问manage.html
3、当前用户必须拥有administrator角色才能访问administrator.html
1.pom.xml 引入依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2.配置类添加 ShiroDialect
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public Account findByUsername(String username) {
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("username",username);
return accountMapper.selectOne(wrapper);
}
}
public interface AccountService {
public Account findByUsername(String username);
}
public interface AccountMapper extends BaseMapper<Account> {
}
测试类
@SpringBootTest
class AccountMapperTest {
@Autowired
private AccountMapper mapper;
@Test
void test(){
mapper.selectList(null).forEach(System.out::println);
}
}
@SpringBootTest
class SpringbootShiroApplicationTests {
@Test
void contextLoads() {
}
}
@SpringBootApplication
@MapperScan("com.fang.mapper")
public class SpringbootShiroApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootShiroApplication.class, args);
}
}
测试
http://www.lhsxpumps.com/_localhost:8080/login 页面
登录成功跳转 index 页面
本文地址:https://blog.csdn.net/liyanfang0310/article/details/107163379
如对本文有疑问, 点击进行留言回复!!
springboot利用profile配置文件进行多环境切换
如何使用MyBatis-Plus代码生成器(逆向工程)一键生成代码
网友评论