复习
- 日志框架
- 作用
- 定位问题(debug、异常、出参入参)
- 跟踪执行流程
- 统计数据
- 如何打日志
- 位置
- controller,入口,整个系统的入口,意味着,用户输入的参数
- 异常
- 业务的关键位置
- 级别
- trace 追踪
- debug 调试
- info 信息
- warn 警告 业务异常
- error 错误 系统异常
- 位置
- spring-boot自带日志框架slf4j+logback,设置日志级别 logging.level.xxx=info
- api使用
- 占位符 {}
- 异常输出 log.error(“错误信息”,exception);
- api使用
- 常用日志框架
- jul
- log4j
- slf4j(接口)+logback(实现)
- log4j2
- 作用
- 安全框架Shiro
- 主要功能
- 认证 authentication authc
- 授权 authorization authz
- 会话管理
- 加密
- 缓存
- shiro示例
- Subject 门面/接口 当前用户
- login 登录
- logout 登出
- isPermitted 是否权限
- hasRole 是否有角色
- SecurityManager 安全管理器,执行认证授权的操作
- UsernamePasswordToken extends AuthenticationToken
- Subject 门面/接口 当前用户
- shiro调用流程
- 主要功能
应用 —-> Subject
↓
SecurityManager
↓
Realm 提供数据源
↓
DB
d. RBAC : Role-Based-Access Control基于角色的权限控制
- 用户
- 用户角色表
- 角色
- 角色权限表
- 权限
e. 自定义Realm
- 继承AuthorizingRealm
- doGetAuthenticationInfo
- -> AuthenticationToken
- <- AuthenticationInfo
- doGetAuthorizationInfo
- -> PrincipalCollection
- <- AuthorizationInfo
继承体系(ctrl+h)
Realm
AuthenticationToken
AuthenticationInfo
异常体系
ShiroException
CacheException
SessionException
AuthenticationException 认证异常
AccountException 账户异常
DisabledAccountException 账户禁用异常
LockedAccountException 账户锁定异常
ConcurrentAccessException 并发访问异常
ExcessiveAttemptsException 尝试次数超限
UnknownAccountException 未知账户异常
CredenctialsException 密码异常
UnSupportedTokenException 不支持的token
AuthorizationException
Shiro集成到spring-boot
sihro集成到web环境下
web环境下的资源:
- 不登录也可以访问
- 登录可以访问
- 登录且必须有相应权限才可以访问
web环境下,将资源近似看做网址
shiro需要检查每一个请求,确定是否需要登录,或者是否需要指定的权限
添加shiro-spring-boot-starter依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.1</version>
</dependency>
修改配置文件
shiro:
loginUrl: /login.html
配置自定义Realm
- 删除掉mybatis,换用注入的UserMapper
- 在配置类中注册自定Realm
- 配置shiro过滤器链 ```java package com.example.demo56.config;
import com.example.demo56.component.MyRealm; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition; import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration public class ShiroConfig {
//shiro自定义Realm
@Bean
public Realm myRealm(){
MyRealm myRealm = new MyRealm();
return myRealm;
}
//shiro过滤器链的配置
// /login ->不需要登录即可访问
// /order ->必须登录才可以访问
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){
ShiroFilterChainDefinition sfcd = new DefaultShiroFilterChainDefinition();
Map<String, String> filterChainMap = sfcd.getFilterChainMap();
//路径 <-> 过滤器的配置缩写
filterChainMap.put("/login","anon");// 使用anon对应的过滤器,过滤/login请求,实质上就放行
filterChainMap.put("/login.html","anon");
filterChainMap.put("/images/**","anon");
filterChainMap.put("/js/**","anon");
filterChainMap.put("/css/**","anon");
filterChainMap.put("/logout","logout");
filterChainMap.put("/**","user");
return sfcd;
}
}
5. 编写登录代码
```java
//前端
登录
<form action="/login" method="post">
<input type="text" name="username" id="username" placeholder="请输入用户名">
<input type="password" name="password" id="password" placeholder="请输入密码">
<input type="submit" value="登录"/>
</form>
//后端
@PostMapping("/login")
public Result login(LoginForm loginForm){
log.info("进入登录{}",loginForm);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(loginForm.getUsername(), loginForm.getPassword()));
return Result.success(subject.getPrincipal());
}catch (AuthenticationException e){
log.info("登录失败",e);
return Result.fail("登录失败:"+e.getMessage());
}
}
配置缩写 | 对应的过滤器 | 功能 |
---|---|---|
*anon | AnonymousFilter | 指定url可以匿名访问 |
authc | FormAuthenticationFilter | 指定url需要form表单登录,默认会从请求中获取username、 password,``rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。 |
authcBasic | BasicHttpAuthenticationFilter | 指定url需要basic登录 |
*logout | LogoutFilter | 登出过滤器,配置指定url就可以实现退出功能,非常方便 |
noSessionCreation | NoSessionCreationFilter | 禁止创建会话 |
perms | PermissionsAuthorizationFilter | 需要指定权限才能访问 |
port | PortFilter | 需要指定端口才能访问 |
rest | HttpMethodPermissionFilter | 将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释 |
roles | RolesAuthorizationFilter | 需要指定角色才能访问 |
ssl | SslFilter | 需要https请求才能访问 |
*user | UserFilter | 需要已登录或“记住我”的用户才能访问 |
授权
注解 ,在需要的方法上添加注解
RequiresAuthentication
RequiresGuest
RequiresPermissions
RequiresRoles
RequiresUser
@GetMapping("/testAuthz")
@RequiresRoles("dev_mgr")
public String testAuthz(){
return "success";
}
@GetMapping("/testAuthz2")
@RequiresPermissions("code:repo:create")
public String testAuthz2(){
return "success";
}
缓存
存在速度较快的介质中,一般是内存
将部分经常被查询的数据,存储在内存中,加快查询速度
查询时,先查询缓存,若无,则查询数据库,并将结果缓存
优点: 加快查询;
缺点:消耗内存;数据不一致问题(加过期时间;更新数据时同步更新缓存)
适用场景:查远多于改;查询频率较高;
授权场景非常适用缓存
//shiro自定义Realm
@Bean
public Realm myRealm(){
MyRealm myRealm = new MyRealm();
myRealm.setCacheManager(cacheManager());
myRealm.setAuthenticationCachingEnabled(true);
myRealm.setAuthorizationCachingEnabled(true);
return myRealm;
}
@Bean
public MemoryConstrainedCacheManager cacheManager(){
return new MemoryConstrainedCacheManager();
}
加密
密码密文存储
- 注册:插入数据库时,需要加密存储
- 登录:对用户输入的数据进行加密,与数据库中的密文比对
步骤:
- 新建一个EncryptUtil,修改数据库密码为密文 ```java package com.example.demo56.common;
import org.apache.shiro.crypto.hash.SimpleHash;
public class EncryptUtil { //加密算法 public static final String ALGORITHM_NAME=”SHA-256”; //迭代次数 public static final int ITERATIONS=1;
public static String encrypt(String source){
//创建一个hash工具,设置了算法及原始数据
SimpleHash simpleHash = new SimpleHash(ALGORITHM_NAME,source);
//设置迭代次数
simpleHash.setIterations(ITERATIONS);
//转换为16进制
return simpleHash.toHex();
}
}
2. 注册一个HashedCredentialsMatcher
```java
@Bean
public CredentialsMatcher hashedCredentialMatcher(){
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(EncryptUtil.ALGORITHM_NAME);
credentialsMatcher.setHashIterations(EncryptUtil.ITERATIONS);
return credentialsMatcher;
}
//shiro自定义Realm
@Bean
public Realm myRealm(){
MyRealm myRealm = new MyRealm();
myRealm.setCacheManager(cacheManager());
myRealm.setAuthenticationCachingEnabled(true);
myRealm.setAuthorizationCachingEnabled(true);
//设置密码比较器
myRealm.setCredentialsMatcher(hashedCredentialMatcher());
return myRealm;
}
- 测试
集成Thymeleaf
后端模板引擎
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--集成shiro到thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
package com.example.demo56.controller;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ThymeleafShiroController {
@GetMapping("toPage")
public String toPage(ModelMap modelMap){
modelMap.put("user", SecurityUtils.getSubject().getPrincipal());
return "success";
}
}
@Bean("shiroDialect")
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>ThymeleafShiro</title>
</head>
<body>
<div>
您好 <span shiro:guest="">游客</span> <span shiro:authenticated="" th:text="${user.username}"></span>
</div>
<div >
<a shiro:hasRole="dev_mgr" href="/fire/developer">开除开发人员</a>
</div>
<div shiro:hasPermission="code:repo:create">
<a href="/core/repo/create">创建代码库</a>
</div>
<div shiro:hasPermission="keepclean">
<a href="/isClean">保洁完成</a>
</div>
</body>
</html>
官方github: https://github.com/theborakompanioni/thymeleaf-extras-shiro#tags
作业
- 讲师管理[后台]
1)提供建表sql
2)spring-boot+mybatis-plus+shiro后端代码,不需要前端
- 讲师入驻
- 讲师带条件的分页查询(姓名,状态[是否审核])
- 审核通过/不通过
- 开除