复习
- 日志框架
- 作用
- 定位问题(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@Beanpublic Realm myRealm(){MyRealm myRealm = new MyRealm();return myRealm;}//shiro过滤器链的配置// /login ->不需要登录即可访问// /order ->必须登录才可以访问@Beanpublic 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@Beanpublic Realm myRealm(){MyRealm myRealm = new MyRealm();myRealm.setCacheManager(cacheManager());myRealm.setAuthenticationCachingEnabled(true);myRealm.setAuthorizationCachingEnabled(true);return myRealm;}@Beanpublic 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@Beanpublic CredentialsMatcher hashedCredentialMatcher(){HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(EncryptUtil.ALGORITHM_NAME);credentialsMatcher.setHashIterations(EncryptUtil.ITERATIONS);return credentialsMatcher;}//shiro自定义Realm@Beanpublic 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;@Controllerpublic 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后端代码,不需要前端
- 讲师入驻
- 讲师带条件的分页查询(姓名,状态[是否审核])
- 审核通过/不通过
- 开除

