SpringBoot整合Shiro
Shiro
可以完成,认证,授权,加密,会话管理,Web集成,缓存等.shiro不依赖任何容器,可以直接运行在javase和javaee中
shiro核心组件
我们会给角色赋予权限,给用户赋予角色
1、UsernamePasswordToken,Shiro 用来封装用户登录信息,使用用户的登录信息来创建令牌 Token。
2、SecurityManager,Shiro 的核心部分,负责安全认证和授权。
3、Suject,Shiro 的一个抽象概念,包含了用户信息。
4、Realm,开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在 Realm 中。
5、AuthenticationInfo,用户的角色信息集合,认证时使用。
6、AuthorzationInfo,角色的权限信息集合,授权时使用。
7、DefaultWebSecurityManager,安全管理器,开发者自定义的 Realm 需要注入到 DefaultWebSecurityManager 进行管理才能生效。
8、ShiroFilterFactoryBean,过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,具体的执行操作就是由 ShiroFilterFactoryBean 创建的一个个 Filter 对象来完成。
shiro入门与SpringBoot整合
创建SpringBoot项目,导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
根据数据库字段创建实体类
@Data
public class Account {
private Integer id;
private String username;
private String password;
private String perms;
private String role;
}
根据实体类创建接口
@Mapper
public interface AccountMapper extends BaseMapper<Account> {
}
配置mysql和mybatisplus
#mysql
spring.datasource.url=jdbc:mysql://localhost:3306/****?useUnicode=true&characterEncoding=utf8
spring.datasource.username=****
spring.datasource.password=****
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#mybatis-plus
mybatis-plus.mapper-locations=classpath:com/mht/springbootmybatisplus/mapper/xml/*.xml
mybatis-plus.type-aliases-package=com.mht.springbootmybatisplus.entity
#关闭驼峰命名法
mybatis-plus.configuration.map-underscore-to-camel-case: false
#mybatis-plus配置控制台打印完整带参数SQL语句
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
编写service层
@Service
public interface AccountService {
public Account findByUsername(String username);
}
Impl
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);
}
}
在shiro中用户认证
编写MyRealm 继承AuthorizingRealm
前端返回的用户密码都封装道token中了,通过token验证用户是否存在,不存在直接返回一个null;
SimpleAuthenticationInfo(account,account.getPassword(),getName());直接把数据丢进去,就会使account.getPassword()和token 中前端返回的密码经行验证
public class MyRealm extends AuthorizingRealm {
@Autowired
private AccountService accountService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//token封转了前端传来的用户名和密码
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//用户名验证
Account account = accountService.findByUsername(token.getUsername());
//用户名不为空就传入到SimpleAuthenticationInfo中验证密码
if(account != null){
return new
//account.getPassword()是正确的通过用户名查询到的密码
SimpleAuthenticationInfo(account,account.getPassword(),getName());
}
return null;
}
}
因为这些东西都需要进行配置,所以需要编写配置类ShiroConfig
编写配置类ShiroConfig
@Configuration
public class ShiroConfig {
// 过滤器工厂 实际去执行的bean
// 通过@Qualifier("manager") DefaultWebSecurityManager manager拿到默认的Web安全管理器
// 设置我们自定义的安全管理器
@Bean
public ShiroFilterFactoryBean filterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager);
return factoryBean;
}
// 2.默认的Web安全管理器
// 通过@Qualifier("myRealm") MyRealm myRealm 从ioc中取出myRealm
// 并且吧自定义的realm配置进去
@Bean
public DefaultWebSecurityManager manager(@Qualifier("myRealm") MyRealm myRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm);
return manager;
}
//1.先把MyRealm注入到spring中
@Bean
public MyRealm myRealm(){
return new MyRealm();
}
}
编写用户认证和授权规则
认证过滤器:
anon:无需认证即可访问,游客身份。
authc:必须认证(登录)才能访问。
authcBasic:需要通过 httpBasic 认证。
user:不一定已通过认证,只要是曾经被 Shiro 记住过登录状态的用户就可以正常发起请求,比如 rememberMe。
授权过滤器:
perms:必须拥有对某个资源的访问权限(授权)才能访问。
role:必须拥有某个角色权限才能访问。
port:请求的端口必须为指定值才可以访问。
rest:请求必须是 RESTful,method 为 post、get、delete、put。
ssl:必须是安全的 URL 请求,协议为 HTTPS。
编写过滤器工厂ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean filterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager);
//权限设置
Map<String,String> map = new HashMap<>();
map.put("/main","authc"); //访问mian必须登录状态
map.put("/manage","perms[manage]");
map.put("/administrator","roles[administrator]");
factoryBean.setFilterChainDefinitionMap(map);
//设置登录页面
factoryBean.setLoginUrl("/login");
//未授权页面
factoryBean.setUnauthorizedUrl("/unauth");
return factoryBean;
}
编写Controller层接口
根据不同的异常返回不同的信息
@Controller
public class MyController {
@PostMapping("/login")
public String login(String username, String password, Model model){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try {
//
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户名错误");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误");
return "login";
}
}
//未授权页面返回信息
@RequestMapping("/unauth")
@ResponseBody
public String unauth(){
return "未授权没有访问权限";
}
}
编写前端页面
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<a href="/main.html">main</a>
<a href="/manage.html">manage</a>
<a href="/administrator.html">administrator</a>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登陆
<form action="/login" method="post">
<table>
<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>
main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>
main
</body>
</html>
manage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>manage</title>
</head>
<body>
manage
</body>
</html>
administrator.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>administrator</title>
</head>
<body>
administrator
</body>
</html>
这时候登陆测试,这时候只要登陆了,就能访问main,但是其他的无法访问;是因为我们没有授权
编写授权doGetAuthorizationInfo()
·@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前登录对象
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;
}
数据库中用户信息如下
这时候我们使用ls登陆就能访问main和manage,使用ww登陆就能全部访问