Shiro
Shiro
是一款主流的 Java 安全框架,不依赖任何容器,可以运行在 Java SE 和 Java EE 项目中,它的主要作用是对访问系统的用户进行身份认证、授权、会话管理、加密等操作。
Shiro 就是用来解决安全管理的系统化框架
Shiro核心组件
用户,角色,权限
会给角色赋予权限,给用户赋予角色
用户拥有角色,角色拥有权限,用户不跟权限直接挂钩
- UsernamePasswordToken,Shiro用来封装用户登录信息,使用户的登录信息来创建令牌Token
- SecurityManager,Shiro的核心部分,负责安全认证和授权
- Suject,Shiro的一个抽象概念,包装了用户信息
- Realm,开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部卸载Realm中
- AuthenticationInfo,用户的角色信息的集合,认证时使用
- AuthorzationInfo,角色的权限信息集合,授权时是用
- DefaultWebSecurityManager,安全管理器,开发者自定义可的Realm,需要注入到DefaultWebSecurityManager进入管理才能生效
- ShiroFilterFactoryBean,过滤器工厂,Shiro的基本运行机制是开发者定制的规则,Shiro去执行,具体的执行操作就是由ShiroFilterFactoryBean创建一个个的Filter对象来完成。
Shiro的运行机制如下图所示。
,S
Token相当于公司的工牌 ,Token关联到Subject,SecurityManager核心部分。关联着角色信息和权限信息。Realm里面写逻辑,然后传到他们两个。
- 创建SpringBoot工程,集合Shiro及其相关组件。
<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-thymeleaf</artifactId>
</dependency>
<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>
- 创建一张表
create table account(
id int(20) not null primary key ,
username varchar(30) null,
password varchar(30) null,
perms varchar(30),
role varchar(30)
)engine=InnoDB default charset=utf8;
要管理数据库,则需要加上数据库的驱动。mysql,这次没有使用mybatis,而是使用mybatis-plus的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
- 创建实体类关联表,entity包
package com.zhou.entity;
import lombok.Data;
@Data
public class Account {
private Integer id;
private String username;
private String password;
private String perms;
private String role;
}
- 创建mapper包,
package com.zhou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhou.entity.Account;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
// 使用mybatis-plus,不用自己写sql,需要继承BaseMapper,泛型是对应的表的类
@Repository
@Mapper // 把mapper中的包也扫进去,也可以在启动类上加注解@MapperScan也可以
public interface AccountMapper extends BaseMapper<Account> {
}
- yml配置数据库和mybatis-plus
# 配数据库
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 测试看是否能查询出来,在测试类写
package com.zhou;
import com.zhou.mapper.AccountMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootShiroApplicationTests {
@Autowired
private AccountMapper accountMapper;
@Test
void contextLoads() {
// 测试接口的查询是否正常
accountMapper.selectList(null).forEach(System.out::println);
}
}
- 创建service层,controller调用service,service调用mapper
接口:AccountService
package com.zhou.service;
import com.zhou.entity.Account;
public interface AccountService {
// 写一个接口,这个接口让它返回一个登录的操作
// 先通过用户名去查询对象
Account findByUserName(String username);
}
实现类先建一个impl包,AccountServiceImpl
package com.zhou.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhou.entity.Account;
import com.zhou.mapper.AccountMapper;
import com.zhou.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public Account findByUserName(String username) {
// 通过用户名查询,需要使用QueryWrapper
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
return accountMapper.selectOne(wrapper);
}
// 然后测试一下这个查询名字的方法对不对,在这个接口处进行测试
}
测试
package com.zhou.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
void findByUsername(){
System.out.println(accountService.findByUserName("ls"));
}
}
- 开始写自己的逻辑,创建一个realm包,根据项目的需求,验证和授权的逻辑全部写在realm包中,自定义过滤器Shiro。
客户端传过来的用户名和密码会直接的封装到token中,先根据token中的用户名进行查询对应的对象,如果对象为null,则说明用户名错误。
如果不为null,说明用户名是正确的,所以只需要验证密码,通过SimpleAuthenticationInfo来验证密码
package com.zhou.realm;
import com.zhou.entity.Account;
import com.zhou.service.AccountService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
// realm用于验证和授权,验证和授权需要继承AuthorizingRealm,作者授权领域
public class AccountShiro extends AuthorizingRealm {
// 把service层的东西注入进来
@Autowired
private AccountService accountService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* token则说明是验证用户和密码
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 验证用户名和密码
// 把AuthenticationToken转成用户名和密码的token
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
// 获取账户的名字,通过名字查询
Account account = accountService.findByUserName(usernamePasswordToken.getUsername());
// 判断对象是否为空
if(account != null){
// 验证密码
return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
}
// 为空的话,直接返回null即可
return null;
}
}
- 配置Shiro。创建Config包,ShiroConfig类
package com.zhou.config;
import com.zhou.entity.Account;
import com.zhou.realm.AccountRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation