1.pom.xml
和之前一樣 只是多加了阿里連接池
com.alibaba
druid
<!--thymeleaf-->
<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>
<!--shiro支持-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--持久層框架mybatis和mysql驅動8.0.9-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--阿里連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.14</version>
</dependency>
<!--插件簡化代碼-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.application.properties
pring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai
#緩存關閉
spring.thymeleaf.cache=false
#mapper包掃描
mybatis.mapper-locations=classpath:mapper/*.xml
#實體類別名
mybatis.type-aliases-package=com.shiro.pojo
#駝峯命名開啓
mybatis.configuration.map-underscore-to-camel-case=true
#session
server.servlet.session.timeout=5
spring.mvc.hiddenmethod.filter.enabled=true
spring.profiles.active=pro
application-pro.properties文件專門放阿里連接池配置
#連接池配置阿里巴巴的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
3.sql
#創建數據庫 shiro
DROP DATABASE IF EXISTS shiro;
CREATE DATABASE IF NOT EXISTS shiro;
#切換數據庫
USE shiro;
#刪
DROP TABLE IF EXISTS USER;
DROP TABLE IF EXISTS role;
DROP TABLE IF EXISTS permission;
DROP TABLE IF EXISTS user_role;
DROP TABLE IF EXISTS role_permission;
#創建
CREATE TABLE USER (
id BIGINT AUTO_INCREMENT,
NAME VARCHAR(100),
PASSWORD VARCHAR(100),
salt VARCHAR(100),
CONSTRAINT pk_users PRIMARY KEY(id)
) CHARSET=utf8 ENGINE=INNODB;
CREATE TABLE role (
id BIGINT AUTO_INCREMENT,
NAME VARCHAR(100),
desc_ VARCHAR(100),
CONSTRAINT pk_roles PRIMARY KEY(id)
) CHARSET=utf8 ENGINE=INNODB;
CREATE TABLE permission (
id BIGINT AUTO_INCREMENT,
NAME VARCHAR(100),
desc_ VARCHAR(100),
url VARCHAR(100),
CONSTRAINT pk_permissions PRIMARY KEY(id)
) CHARSET=utf8 ENGINE=INNODB;
CREATE TABLE user_role (
id BIGINT AUTO_INCREMENT,
uid BIGINT,
rid BIGINT,
CONSTRAINT pk_users_roles PRIMARY KEY(id)
) CHARSET=utf8 ENGINE=INNODB;
CREATE TABLE role_permission (
id BIGINT AUTO_INCREMENT,
rid BIGINT,
pid BIGINT,
CONSTRAINT pk_roles_permissions PRIMARY KEY(id)
) CHARSET=utf8 ENGINE=INNODB;
#插入
INSERT INTO `permission` VALUES (1,'addProduct','增加產品','/addProduct');
INSERT INTO `permission` VALUES (2,'deleteProduct','刪除產品','/deleteProduct');
INSERT INTO `permission` VALUES (3,'editeProduct','編輯產品','/editeProduct');
INSERT INTO `permission` VALUES (4,'updateProduct','修改產品','/updateProduct');
INSERT INTO `permission` VALUES (5,'listProduct','查看產品','/listProduct');
INSERT INTO `permission` VALUES (6,'addOrder','增加訂單','/addOrder');
INSERT INTO `permission` VALUES (7,'deleteOrder','刪除訂單','/deleteOrder');
INSERT INTO `permission` VALUES (8,'editeOrder','編輯訂單','/editeOrder');
INSERT INTO `permission` VALUES (9,'updateOrder','修改訂單','/updateOrder');
INSERT INTO `permission` VALUES (10,'listOrder','查看訂單','/listOrder');
INSERT INTO `role` VALUES (1,'admin','超級管理員');
INSERT INTO `role` VALUES (2,'productManager','產品管理員');
INSERT INTO `role` VALUES (3,'orderManager','訂單管理員');
INSERT INTO `role_permission` VALUES (1,1,1);
INSERT INTO `role_permission` VALUES (2,1,2);
INSERT INTO `role_permission` VALUES (3,1,3);
INSERT INTO `role_permission` VALUES (4,1,4);
INSERT INTO `role_permission` VALUES (5,1,5);
INSERT INTO `role_permission` VALUES (6,1,6);
INSERT INTO `role_permission` VALUES (7,1,7);
INSERT INTO `role_permission` VALUES (8,1,8);
INSERT INTO `role_permission` VALUES (9,1,9);
INSERT INTO `role_permission` VALUES (10,1,10);
INSERT INTO `role_permission` VALUES (11,2,1);
INSERT INTO `role_permission` VALUES (12,2,2);
INSERT INTO `role_permission` VALUES (13,2,3);
INSERT INTO `role_permission` VALUES (14,2,4);
INSERT INTO `role_permission` VALUES (15,2,5);
INSERT INTO `role_permission` VALUES (50,3,10);
INSERT INTO `role_permission` VALUES (51,3,9);
INSERT INTO `role_permission` VALUES (52,3,8);
INSERT INTO `role_permission` VALUES (53,3,7);
INSERT INTO `role_permission` VALUES (54,3,6);
INSERT INTO `role_permission` VALUES (55,3,1);
INSERT INTO `role_permission` VALUES (56,5,11);
INSERT INTO `user` VALUES (1,'zhang3','a7d59dfc5332749cb801f86a24f5f590','e5ykFiNwShfCXvBRPr3wXg==');
INSERT INTO `user` VALUES (2,'li4','43e28304197b9216e45ab1ce8dac831b','jPz19y7arvYIGhuUjsb6sQ==');
INSERT INTO `user_role` VALUES (43,2,2);
INSERT INTO `user_role` VALUES (45,1,1);
#getPassword 方法: 查密碼
SELECT PASSWORD FROM USER WHERE NAME ='li4'
# listRoles 方法: 根據用戶名查詢此用戶有哪些角色,這是3張表的關聯
SELECT r.name FROM USER u
LEFT JOIN user_role ur ON u.id = ur.uid
LEFT JOIN Role r ON r.id = ur.rid
WHERE u.name = 'zhang3';
#listPermissions 方法:根據用戶名查詢此用戶有哪些權限,這是5張表的關聯
SELECT p.name FROM USER u
LEFT JOIN user_role ru ON u.id = ru.uid
LEFT JOIN role r ON r.id = ru.rid
LEFT JOIN role_permission rp ON r.id = rp.rid
LEFT JOIN permission p ON p.id = rp.pid
WHERE u.name ='li4'
4.通過idea連接數據庫快速創建pojo類,導包名修改一下即可
5.UserDao 和UserService(省略)
@Repository
@Mapper
public interface UserDao {
//根據用戶名查密碼
@Select ("SELECT PASSWORD FROM USER WHERE NAME =#{name}")
String getPwdByName(String name);
//三表查詢,根據用戶名查詢角色
List<String> listRoles(String name);
//根據用戶名查詢 權限 五表查詢
List<String> listPermissions(String name);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
public String getPwdByName(String name) {
return userDao.getPwdByName (name);
}
@Override
public List<String> listRoles(String name) {
return userDao.listRoles (name);
}
@Override
public List<String> listPermissions(String name) {
return userDao.listPermissions (name);
}
}
6.接下來是修改認證邏輯:
/*授權和認證邏*/
public class CustomRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println ("執行授權邏輯");
SimpleAuthorizationInfo simpleInfo = new SimpleAuthorizationInfo ( );
//獲取登錄用戶名
String name = (String) principalCollection.getPrimaryPrincipal ( );
//之後在數據庫查詢他是什麼角色 什麼權限一一添加他們
System.out.println ("查詢角色和權限:" + name);
List<String> listRoles = userService.listRoles (name);
// simpleInfo.addRoles (listRoles);直接添加多個角色或者forench添加
for (String role : listRoles) {
simpleInfo.addRole (role);
}
//添加角色下的權限 比如crud
List<String> listPermissions = userService.listPermissions (name);
simpleInfo.addStringPermissions (listPermissions);
//設置好權限返回
return simpleInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//加這一步的目的是在Post請求的時候會先進認證,然後在到請求
if (null == authenticationToken.getPrincipal ( )) {
return null;
}
System.out.println ("執行認證邏輯");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//獲取用戶登陸的名
String name = token.getUsername ( );
String pwd = userService.getPwdByName (name);
if (null == pwd) {
//用戶名不存在
return null;//拋出一個空的對象 拋出異常UnknowAccountException
}
//這裏驗證authenticationToken和simpleAuthenticationInfo的信息
return new SimpleAuthenticationInfo (name, pwd, getName ( ));
}
}
7.Shiro配置:
/*常用的過濾器
* anon:無認證
* authc:必須認證 登陸即可
* user: 使用記住我可以直接訪問
* perms: 必須有資源權限 比如crud
* roles: 必須有角色權限
* */
@Configuration
public class ShiroConfig {
/**
* 創建自定義配置的Realm
*/
@Bean
CustomRealm myRealm() {
return new CustomRealm ( );
}
/**
* 創建DefaultWebSecurityManager管理器,使它管理自定義的Realm
*/
@Bean
DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager ( );
manager.setRealm (myRealm ( ));
return manager;
}
/**
*創建shiroFilterFactoryBean
* 關聯一個securityManager ( )管理器
*/
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean ( );
bean.setSecurityManager (securityManager ( ));
//登陸頁
bean.setLoginUrl ("/login");
//登陸成功後界面
bean.setSuccessUrl ("/index");
//未授權跳轉到
bean.setUnauthorizedUrl ("/tip");
Map<String, String> map = new LinkedHashMap<> ( );
//anon是把限制權限改爲無限制
//map.put ("/index", "anon");
//authc 登陸後可以訪問
// map.put ("/**", "authc");
map.put ("/add", "authc");
//權限必須有addProduct纔可以訪問
map.put ("/update","perms[addProduct]");
//角色是admin 纔可以訪問超級管理員界面
map.put ("/admin","roles[admin]");
bean.setFilterChainDefinitionMap (map);
return bean;
}
}
8.controller
@Controller
public class LoginController {
//表單提交,處理後返回首頁
@PostMapping("/tologin")
String tologin(String name, String password, Model model) {
//處理邏輯
/**使用Shiro編寫認證操作*/
//1.獲取Subject
Subject subject = SecurityUtils.getSubject ( );
//2.封裝用戶數據
UsernamePasswordToken token = new UsernamePasswordToken (name, password);
//執行登陸方法
try {
subject.login (token);
//登陸信息會交給Realm去執行認證邏輯,比如去數據庫對比用戶,密碼
//然後會設置角色,權限
} catch (UnknownAccountException e) {
model.addAttribute ("msg", "用戶名不存在");
return "/login";
} catch (IncorrectCredentialsException e) {
model.addAttribute ("msg", "密碼錯誤");
return "/login";
}
//這裏的請求不能返回"/index"這是返回index.html了 沒有經過mvc攔截到
// 必須使用"redirect:/index" 或者"forward:/index"
//catch塊可以跳轉是因爲我的頁面就叫login.html 請求也是/login
return "redirect:/index";
}
@RequestMapping("/tip")
@ResponseBody
String tip(){
return "抱歉,您沒有權限訪問該頁面!";
}
}
@Controller
public class UserController {
@GetMapping("/login")
String login() {
return "login";
}
@RequestMapping("/index")
String test() {
return "test";
}
@GetMapping("/add")
String add() {
return "add";
}
@GetMapping("/update")
String update() {
return "update";
}
@ResponseBody
@GetMapping("/admin")
String admin() {
return "歡迎您超級管理者!";
}
}
路徑是這樣的先請求/index 返回到首頁展示三鏈接(/add,/update,/admin)
點擊後後被攔截到登陸頁/login 賬號密碼post提交後到/tologin 數據庫查詢無誤後返回首頁,有誤返回登陸頁
這時候可以訪問三鏈接 有權限放行 沒有則跳轉到提示信息/tip
9.html
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶添加頁面</title>
</head>
<body>
用戶添加
</body>
</html>
update.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶更改的頁面</title>
</head>
<body>
用戶更改
</body>
</html>
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶的測試頁面</title>
</head>
<body>
<h2 th:text="${name}"></h2>
<hr/>
<p> 進入用戶添加功能:<a href="/add">用戶添加,登陸就可以操作</a></p>
<p>進入用戶更新功能:<a href="/update">用戶更新,擁有addProduct權限可以操作</a></p>
<p><a href="/admin">超級管理admin角色界面</a></p>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陸頁面</title>
</head>
<body>
<h3 th:text="${msg}" style="color: red"></h3>
<form method="post" action="/tologin">
用戶名:<input type="text" name="name"/> <br/>
密碼:<input type="password" name="password"><br/>
<input type="submit" value="登陸">
</form>
</body>
</html>
10.測試
有兩個用戶 zhang3爲admin 角色 有許多權限可以查sql li4是個經理 沒有admin權限
10.1.亂輸一個
10.2.登陸張三,然後訪問三界面:全部可以
10.3.換成li4登陸.在訪問:
/admin返回了302狀態 然後發給了/tip
**補上mapper.xml:**可以通過選中dao名按住ALT+enter快速創建映射,再選擇方法名創建…
<?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.shiro.dao.UserDao">
<select id="listRoles" resultType="java.lang.String">
SELECT r.name FROM USER u
LEFT JOIN user_role ur ON u.id = ur.uid
LEFT JOIN Role r ON r.id = ur.rid
WHERE u.name =#{name}
</select>
<select id="listPermissions" resultType="java.lang.String">
SELECT p.name FROM USER u
LEFT JOIN user_role ru ON u.id = ru.uid
LEFT JOIN role r ON r.id = ru.rid
LEFT JOIN role_permission rp ON r.id = rp.rid
LEFT JOIN permission p ON p.id = rp.pid
WHERE u.name =#{name}
</select>
</mapper>
有錯誤的話歡迎指正!