本案例实战框架用了以下技术
SpringBoot框架
Shiro的框架
MyBatis框架
Spring框架
thymeleaf技术
SSM三层框架
数据库
实操部分
Subject——org.apache.shiro.subject.Subject
特定于当前与软件交互的实体的安全视图
SecurityManager——org.apache.shiro.mgt.SecurityManager
Shiro架构的核心,管理并协调各个组件共同完成安全工作,它还管理每个应用程序用户的Shiro视图,因此它知道如何为每个用户执行安全操作。
Authenticator——org.apache.shiro.authc.Authenticator
Authenticator是负责认证的组件,当用户尝试登录时,登录是由Authenticator来执行的,Authenticator将会从Realms取出用户信息,来和用户提供的登录信息进行比对
Authenticator Strategy——org.apache.shiro.authc.pam.AuthenticationStrategy
如果超过一个Realm被配置了,这个AuthenticationStrategy将会协调这些Realms来决定认证操作是否成功,比如,认证成功是需要所有的Realms都认证成功,还是仅仅一个Realm成功
Authorizer——org.apache.shiro.authz.Authorizer
Authorizer是负责访问控制的组件,换句话说,它负责检测用户是否有执行某个操作的权限。和Authenticator一样,Authorizer也从多个Realms数据源中获取用户的角色信息和权限信息,通过这些信息来判断用户是否可以执行某个操作
SessionManager——org.apache.shiro.session.mgt.SessionManager
注意不是SecurityManager,SessionManager负责创建和管理用户的Session周期。在安全框架中,Shiro提供了一个独有的特性,Shiro可以在任何环境中管理用户会话,即使实在非Web/Servlet和非EJB容器环境中,默认情况下,Shiro将会使用已有的会话机制,如Servlet容器,如果没有,Shiro将会使用内建的企业会话管理机制来管理会话。通过SessionDao,我们可以使用任何数据源来持久化Session
SessionDAO——org.apache.shiro.session.mgt.eis.SessionDAO
SessionDAO代表SessionManager执行会话持久性(CRUD)操作。它允许将任何数据存储插入到Session Management基础结构中
CacheManager——org.apache.shiro.cache.CacheManager
CacheManager负责创建Shiro中的其他组件的Cache实例并管理其生命周期,因为Shiro需要访问各种数据源来进行认证,授权和会话管理,缓存一直是框架中的一流架构特性,可以在使用这些数据源时提高性能,任何现代的开源或者企业缓存产品都可以接入Shiro中
Cryptography——org.apache.shiro.crypto.*
Shiro的crypto包包含了易于使用和理解的常见加密算法实现。使用过Java自带的加密库的人都应该知道它很难使用,Shiro提供的加密API简化了复杂的Java机制,让加密更便于使用
Realms——org.apache.shiro.realm.Realm
正如前面提到的,Realms是Shiro和应用程序数据的桥梁,当需要执行认证和授权时,Shiro会从至少一个Realm中寻找用户信息,应用程序中可以配置许多个Realm,Shiro会在必要时协调这些Realm
pom.xml文件信息配置
<?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.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lop</groupId>
<artifactId>shiro-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shiro-springboot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- mybatis整合SpringBoot-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!-- druids数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.12</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<!-- MySql的依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.9.1</version>
</dependency>
<!-- Thymeleaf模板-->
<!--Thymeleaf 说明基于3.0.1 模板引擎-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
<version>2.5.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Yml文件信息
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
initial-size: 5
min-idle: 5
max-active: 20
# 配置获取连接等待超时的时间
max-wait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 最好的功能
max-pool-prepared-statement-per-connection-size: 20
filters: stat,wall
use-global-data-source-stat: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
index.html页面
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http:www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div shiro:hasPermisssion="user:add">
假如hasPermisssion有这个限制
<a th:href="@{/user/add}">add</a>
</div>
<div align="center">
<h1>index</h1>
<!--/*@thymesVar id="msg" type="java"*/-->
<p th:text="${msg}"></p>
<a th:href="@{/user/add}">add</a>
<br>
<a th:href="@{/user/update}">update</a>
</div>
</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>
<!--从Session判断值-->
<div align="center" th:if="session.loginUser==null">
<h1>登录</h1>
<hr>
<!--/*@thymesVar id="msg" type="java"*/-->
<h2 th:text="${msg}" style="color: red;"></h2>
<form th:action="@{/login}" method="post">
<p>用户名:<input type="text" name="username" placeholder="请输入用户名"></p>
<p>用户密码:<input type="password" name="password" placeholder="请输入用户密码"></p>
<p><input type="submit"></p>
</form>
</div>
</body>
</html>
数据层:
<?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">
<!--比要时候要加上-->
<!--suppress ALL -->
<mapper namespace="com.lop.mapper.UserMapper">
<select id="queryUserByName" parameterType="String" resultType="User">
select * from mybatis.user where name =#{name}
</select>
</mapper>
package com.lop.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface UserMapper {
public UserMapper queryUserByName(String username);
}
Service层:
package com.lop.Service;
import com.lop.pojo.User;
public interface UserService {
public User queryUserByName(String username);
}
package com.lop.Service;
import com.lop.mapper.UserMapper;
import com.lop.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceLmp implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User queryUserByName(String username) {
userMapper.queryUserByName(username);
return null;
}
}
控制层:
package com.lop.Controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
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 MyController {
@RequestMapping({"/", "/index"})
public String toIndex(Model model) {
model.addAttribute("msg", "hllow");
return "index";
}
@RequestMapping("user/add")
public String add() {
return "user/add";
}
@RequestMapping("user/update")
public String update() {
return "user/update";
}
@RequestMapping("/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 "index";
} catch (UnknownAccountException e) {
model.addAttribute("msg", "用户的名字步存在");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "用户的密码有误");
return "login";
}
}
@RequestMapping("/noauth")
public String unauthorized(){
return "没有授权无法访问信息";
}
}
认证授权
package com.lop.config;
import com.lop.Service.UserService;
import com.lop.pojo.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 自定义的UserRealm
*/
public class UserRealm extends AuthorizingRealm {
//把用户数据库拿过来 连接真实数据库
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("----------------------授权");
// SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//打开没有授权的功能
info.addStringPermission("uder:add");
//拿到当前对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();//拿到user对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("==========================认证");
// String name = "root";
// String password = "123456";
UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
//连接真实数据库
User user=userService.queryUserByName(usertoken.getUsername());
if (user == null) {
return null;
}
Subject cursubject = SecurityUtils.getSubject();
Session session = cursubject.getSession();
session.setAttribute("loginUser",user);
// if(!usertoken.getUsername().equals(name)){
// return null;
// }
//密码认证
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
ShiroConfig 代码解读:
package com.lop.config;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
设置管理器
bean.setSecurityManager(defaultWebSecurityManager);
//授权的信息内容 有user:add 没有授权
filterMap.put("/user/add", "perms[user:add]");
filterMap.put("/user/update", "perms[user:update]");
//用户没有授权请前往这个页面
bean.setLoginUrl("/noauth");
//设置登录请求
bean.setLoginUrl("/tologin");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
// 自定义的UserRealm
// @Bean(name = "userRealm")
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http:www.thymeleaf.org/thymeleaf-extras-shiro">
<div shiro:hasPermisssion="user:add">
假如hasPermisssion有这个限制
<a th:href="@{/user/add}">add</a>
</div>
<div align="center">
<h1>index</h1>
<!--/*@thymesVar id="msg" type="java"*/-->
<p th:text="${msg}"></p>
<a th:href="@{/user/add}">add</a>
<br>
<a th:href="@{/user/update}">update</a>
</div>