Shiro 简介
- shiro中文文档
- Shiro的架构主要有三个顶级概念:
Subject
,SecurityManager
和Realms
名称 | 概念 |
---|---|
Subject | ”用户“,也可以指操作应用的人,第三方的应用, 当前正在与软件交互的事物 |
SecurityManager | 是 Shiro 体系结构的核心,并充当一种“伞”对象,该对象协调其内部安全组件,一起形成对象图。就是管理shiro中的各种组件,如,subject,realm等等 |
Realms | 充当 Shiro 与您的应用程序安全数据之间的“bridge 梁”或“连接器”。就是整用户名和密码的 |
关键点
- 经典的图
- 属于shiro的东西都归Security Manager管
- 所以写代码时,将shiro的东西与Security Manager绑定是核心
- 最主要的权限管理的一定要有:
Subject
,SecurityManager
和Realms
样例
- 导入依赖(要用Mybatis和
Druid
数据源)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBootShiroMybatis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 配置文件(用
Druid
数据源和配置Mybtis)
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
#SpringBoot默认是不注入这些的,需要自己绑定
#druid数据源专有配置
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity
#则导入log4j 依赖就行
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
type-aliases-package: com.boot.pojo
mapper-locations: classpath:mapper/*.xml
- 实体类pojo(
User [id,name, password,perm]
) ,略
- 各种Mapper(就用一个方法)
User getUserByName(String name);
- 配置类config,有两个
@Configuration
public class ShiroConfig {
//shiroFactoryBean
//在基于Spring的Web应用程序中使用FactoryBean来定义主Shiro过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("webSecurityManager") DefaultWebSecurityManager webSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(webSecurityManager);//设置SecurityManager,管理过滤器
//添加shiro内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证了才能让问
user:必颈拥有记往我功能才能用
perms :拥有对某个资源的权限才能访问;
role:拥有某个角色权限才能访问
*/
Map<String, String> filterMap = new LinkedHashMap<>();
//filterMap.put("/user/add","authc");
//需要认证才能访问这个链接/user/add
//filterMap.put("/user/update","authc");
//拦截
//设置不同权限名允许访问的地方
filterMap.put("/user/add","perms[user:add]");//[括号]里面就是某种权限的名字,可以随意
filterMap.put("/user/update","perms[user:update]"); //这个权限可以访问/user/update
//filterMap.put("/user/*","authc");
//要注意filterMap.put的顺序,上面的权限大,下面权限小的不执行
bean.setFilterChainDefinitionMap(filterMap);
bean.setLoginUrl("/toLogin");//设置登录请求
bean.setUnauthorizedUrl("/unAuth");//设置未授权页面
return bean;
}
//SecurityManager需要关联realm
@Bean
public DefaultWebSecurityManager webSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);//将userRealm和securityManager关联
return securityManager;
}
//创建realm
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// principal就是账户主体,本例就是user
System.out.println("执行了授权");
User user = (User)principals.getPrimaryPrincipal();
System.out.println(user.getPerm());
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//simpleAuthorizationInfo.addStringPermission("user:add");
// Subject subject = SecurityUtils.getSubject();
// User currentUser= (User) subject.getPrincipal();//拿到user
//simpleAuthorizationInfo.addStringPermission((String) principals.getPrimaryPrincipal());//从数据库中获取权限
simpleAuthorizationInfo.addStringPermission(user.getPerm());
return simpleAuthorizationInfo;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行的认证");
// String username="root";
// String password="123456";
// UsernamePasswordToken userToken= (UsernamePasswordToken) token;
// if (!userToken.getUsername().equals(username))
// {
// return null;//抛出异常到subject所在地
// }
// //密码认证,shiro干活
// return new SimpleAuthenticationInfo("",password,"");
UsernamePasswordToken userToken= (UsernamePasswordToken) token;
User user=userService.getUserByName(userToken.getUsername());//查数据库,realm的职责就是这个
if (user==null){//认证不通过
return null;//返回null,让subject.login(token),抛出异常
}
//将user,丢到SimpleAuthenticationInfo,以便通过subject获取
return new SimpleAuthenticationInfo(user,userToken.getPassword(),"");
//将查到的pojo变成账户主体(principal),以及匹配密码,将密码进行哈希盐加密
//principals,存在session中
}
}
- controller
@Controller
public class ShiroController {
@GetMapping({"/","index"})
public String index(Model model){
model.addAttribute("msg","hello Shiro");
return "index";
}
@GetMapping("/user/add")
public String add(){
return "user/add";
}
@GetMapping( "/user/update")
public String update(){
return "user/update";
}
@GetMapping("/toLogin")
public String toLogin(){
return "login";
}
@PostMapping("/login")
public String login(String username,String password,Model model){
Subject subject = SecurityUtils.getSubject();//从线程获得subject,
/*
运行中,SubjectThreadState对象,这个对象很重要,
主要负责将subject和securityManager通过ThreadContext对象绑定到当前线程中
*/
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
//将username和password,打包成令牌
try {
subject.login(usernamePasswordToken);//通过login携带令牌进入认证(authenticate)
return "index";
}
catch (UnknownAccountException | IncorrectCredentialsException e){//用户名不存在,或密码错误
model.addAttribute("msg","用户名或密码错误");
return "login";
}
}
@GetMapping("/unAuth")
@ResponseBody
public String unAuthorized(){
return "未授权,请充值一百万";
}
}
- 前端目录
注释很关键