Shiro入门
Shiro的使用
今天学习了Shiro框架,感觉不是很懂,有点懵,写篇随笔记录一下
1. Shiro三个核心组件
1.1 Subject
Subject:即“当前操作用发户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
1.2 SecurityManager
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
1.3 Realm
Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
2. Shiro相关类介绍
(1)Authentication 认证 ---- 用户登录
(2)Authorization 授权 --- 用户具有哪些权限
(3)Cryptography 安全数据加密
(4)Session Management 会话管理
(5)Web Integration web系统集成
(6)Interations 集成其它应用,spring、缓存框架
3. Shiro 特点
(1)易于理解的 Java Security API;
(2)简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
(3)对角色的简单的签权(访问控制),支持细粒度的签权;
(4)支持一级缓存,以提升应用程序的性能;
(5)内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境;
(6)异构客户端会话访问;
(7)非常简单的加密 API;
(8)不跟任何的框架或者容器捆绑,可以独立运行
一. 新建SpringBoot项目,导入相关依赖,
<!--
Subject 用户
SecurityManager 管理所有用户
Realm 连接数据
-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
二. 创建数据库、实体类以及mapper配置文件(为了方便直接使用上一篇整合Myabtis的数据库和文件)
1.创建数据库表
DROP DATABASE IF EXISTS mybatis;
CREATE DATABASE mybatis;
DROP TABLE IF EXISTS USER;
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) NOT NULL COMMENT '姓名',
PASSWORD VARCHAR(32) NOT NULL COMMENT '密码',
perms VARCHAR(32) NOT NULL COMMENT '权限参数'
)ENGINE=INNODB AUTO_INCREMENT=1 CHARSET=utf8;
INSERT INTO USER(username,PASSWORD,perms) VALUES("niubi","123456","user-add");
INSERT INTO USER(username,PASSWORD,perms) VALUES("wocao","123456","user-update");
INSERT INTO USER(username,PASSWORD) VALUES("Java","123456");
INSERT INTO USER(username,PASSWORD) VALUES("python","123456");
INSERT INTO USER(username,PASSWORD) VALUES("C","123456");
2.创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private String perms;
}
3.创建UserMapper接口
@Mapper//表示本类是一个Mybatis的mapper
@Repository//不加也可以,但是Controller会报错,但是不影响正常运行
public interface UserMapper {
//根据用户名查询用户
User findUserByUserName(String username);
}
4.编写UserMapper.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gaoteng.mapper.UserMapper">
<select id="findUserByUserName" resultType="User">
select * from user where username = #{username}
</select>
</mapper>
5.编写application.properties配置文件
# 配置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 开启别名映射
mybatis.type-aliases-package=com.gaoteng.pojo
# mapper的路径 就是xml配置文件中的<mappers>标签
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
三. 编写前端页面
首页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<a th:href="@{/user/toLogin}" shiro:guest="">登录</a>
<a th:href="@{/user/logout}" style="float: right">注销用户</a>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a><br>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>登录</h2>
<p th:text="${msg}"></p>
<hr>
<form method="post" th:action="@{/login}">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
upodate.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
四.新建config包,创建ShiroConfig配置文件和UserRealm类
package com.gaoteng.config;
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.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean bean= new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
//添加Shiro的内置过滤器
/*
* anon:无需认证就能访问
* authc:必须认证了才能访问
* user:必须拥有 记住我 功能才能访问
* perms:拥有对某个资源的权限才能访问
* roles:拥有某个角色权限才能访问
* logout:注销
* */
//拦截
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/add","authc");
filterMap.put("/user/update","authc");
//注销
filterMap.put("/user/logout","logout");
//授权 正常情况下,没有授权就会跳转到未授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录的请求
bean.setLoginUrl("/user/toLogin");
//设置未授权页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
@Bean
@Qualifier("securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Autowired UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
package com.gaoteng.config;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserMapper userMapper;//这里为了方便演示,没有编写Service层
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//给所有用户增加user:add权限
//info.addStringPermission("user:add");
//拿到当前登录的用户对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证");
//用户名和密码
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
//获取用户名
String username = userToken.getUsername();
//根据用户名查询用户是否存在
User user = userMapper.findUserByUserName(username);
if(user==null){
return null;//抛出UnknownAccountException异常
}
//密码是由Shiro来处理
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
四. 编写Cotroller
package com.gaoteng.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TestController {
@RequestMapping({"/","/index","/index.html"})
public String index(Model model){
model.addAttribute("msg","niubi");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "add";
}
@RequestMapping("/user/update")
public String update(){
return "update";
}
@RequestMapping("/user/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/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 "redirect:index";
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户名不存在");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "未经授权,无法访问";
}
//注销 如果注销操作中需要额外的操作时,可以自己编写Controller处理,如果只需要简单的注销功能,
//只需要在Shiro的内置过滤器中添加类似于filterMap.put("/user/logout","logout");的代码
/* @RequestMapping("/user/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}*/
}
因为运行要演示的图片太多,所以这里就不演示了.
不过我现在只知道怎么使用,并不了解原理.如果想了解原理的话,可以请阅读其它文章.
本篇随笔借鉴了以下文章或视频:[权限框架之Shiro详解(非原创)](https://www.cnblogs.com/WUXIAOCHANG/p/10886534.html)
springboot整合shiro -shiro介绍(一)
【狂神说Java】SpringBoot最新教程IDEA版通俗易懂