快速搭建Spring Boot + Apache Shiro 环境
个人博客网:https://wushaopei.github.io/ (你想要这里多有)
一、Apache Shiro 介绍及概念
概念:Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Shiro 框架图:

框架图解析:
Primary Concerns四部分: 用户识别、授权、会话管理、加密
Supporting Features 五部分: Shiro特有的API、缓存、 并发、测试、Run As 、Remember Me
Shiro 概念层 架构:

应用代码经过 Subject 进行简单授权和认证, Subject 又委托给SecurityManager
二、Apache Shiro 部分功能讲解:
1、Apache Shiro授权讲解

- 首先调用Subject 进行处理,再委托给 Security Manageer ,Security Manageer会委托给 Authorizer ,Authorizer是真正的授权者。
Shiro 的授权主要包含四个核心:主体、资源、权限、角色
主体: 访问应用的用户,用户只有授权后才能访问到相应的资源
资源: 查看、编辑某个数据,读取某个文本等都是资源
权限: 建立在资源以及操作上,只限定能做什么
角色: 才是真正可以对主体的权限进行分配的核心
2、 Apache Shiro权限拦截框架图:

3、Apache Shiro会话管理框架图

会话管理主要是管理所有会话的创建、查询、交互等。
三、基于SpringBoot的Apache Shiro环境快速搭建与配置
1、环境搭建及使用
主要涉及两点:
- 快速搭建Spring Boot + Apache Shiro 环境
- 常用Case实现
(1)创建springboot 工程 Spring-boot_Shiro ,整合 Shiro 依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.20</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
(2)创建数据库表结构 : user 、role、permission、permission_role、user_role、五张表;
CREATE TABLE `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT ,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
PRIMARY KEY (`uid`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=3
ROW_FORMAT=DYNAMIC;
CREATE TABLE `role` (
`rid` int(11) NOT NULL AUTO_INCREMENT ,
`rname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
PRIMARY KEY (`rid`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=3
ROW_FORMAT=DYNAMIC;
CREATE TABLE `permission` (
`pid` int(11) NOT NULL AUTO_INCREMENT ,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
`url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' ,
PRIMARY KEY (`pid`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=1
ROW_FORMAT=DYNAMIC;
CREATE TABLE `permission_role` (
`rid` int(11) NOT NULL ,
`pid` int(11) NOT NULL ,
INDEX `idx_rid` (`rid`) USING BTREE ,
INDEX `idx_pid` (`pid`) USING BTREE
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=DYNAMIC;
CREATE TABLE `user_role` (
`uid` int(11) NOT NULL ,
`rid` int(11) NOT NULL ,
INDEX `idx_uid` (`uid`) USING BTREE ,
INDEX `idx_pid` (`rid`) USING BTREE
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=DYNAMIC;
(3)创建 user、role、permission 三个类
public class User {
private String username;
private Integer uid;
private String password;
private Set<Role> roles = new HashSet<>();
。。。。。。。。。。。。
}
public class Role {
private Integer rid;
private String rname;
private Set<Permission> permissionSet = new HashSet<>();
...................
}
public class Permission {
private Integer pid;
private String name;
private String url;
。。。。。。。。。。。。。。
}
(4)创建service业务层、mapper 持久层
service 、serviceimpl业务接口及实现类
public interface UserService {
User findByUsername(String username);
}
@Service
@Transactional(readOnly=true)
public class UserServiceimpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findByUsername(String username) {
return userMapper.findByUsername(username);
}
}
UserMapper.java 持久层
@Component
@Mapper
public interface UserMapper {
User findByUsername(@Param("username") String username);
List<User> getAll();
}
UserMapper.xml
<mapper namespace="com.webcode.springboot.mappers.UserMapper">
<resultMap id="userMap" type="com.webcode.springboot.entities.User">
<id property="uid" column="uid"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<collection property="roles" ofType="com.webcode.springboot.entities.Role">
<id property="rid" column="rid"/>
<result property="rname" column="rname"/>
<collection property="permissionSet" ofType="com.webcode.springboot.entities.Permission">
<id property="pid" column="pid"/>
<result property="name" column="name"/>
<result property="url" column="url"/>
</collection>
</collection>
</resultMap>
<select id="findByUsername" parameterType="string" resultMap="userMap">
SELECT u.*,r.*,p.*
from user u
INNER JOIN user_role ur on ur.uid = u.uid
INNER JOIN role r on r.rid = ur.rid
INNER JOIN permission_role pr on pr.rid = r.rid
INNER JOIN permission p on pr.pid = p.pid
WHERE u.username = #{username}
</select>
<select id="getAll"
resultMap="userMap">
select *
from USER
</select>
</mapper>
(5)配置mapper.xml 包扫描位置及对应的javabean 所在包,用于mapper.xml的映射
application.yml 配置文件:
mybatis:
mapper-locations: classpath*:/mybatis/mappers/*Mapper.xml #包扫描 mapper.xml 类
type-aliases-package: com.webcode.springboot.entities # 指定相应 mapper.xml 中实体 javabean 的所在位置
server:
port: 8081
Bootstrap 启动类:
//扫描Mapper接口
@MapperScan("com.webcode.springboot.mappers")
@ComponentScan({"com.webcode.springboot"})
@SpringBootApplication
public class Bootstrap { public static void main(String[] args) {
SpringApplication.run(Bootstrap.class, args);
}
}
2、基于Apache Shiro权限管理Case -- 认证授权配置
创建自定义的 AuthRealm 类,该类 继承 AuthorizingRealm 类,并重写doGetAutherizationInfo(PrincipalCollection principals) 和 doGetAuthenticationInfo(AuthenticationToken token) 两个方法 分别 作为授权和认证登录所用;
public class AuthRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 授权要先从 session 取出对象来
User user = (User) principals.fromRealm(this.getClass().getName()).iterator().next();
List<String> permissionList = new ArrayList<>();
Set<Role> roleSet = user.getRoles();
if (CollectionUtils.isNotEmpty(roleSet)){
for (Role role : roleSet) {
Set<Permission> permissionSet = role.getPermissionSet();
if (CollectionUtils.isNotEmpty(permissionSet)){
for (Permission permission : permissionSet) {
permissionList.add(permission.getName());
}
}
}
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(permissionList);
return null;
}
// 认证登录
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; //将Token 降为 usernamePasswordToken
String username = usernamePasswordToken.getUsername(); //去除其中的username
User user = userService.findByUsername(username); //根据username 查询到 user 信息
return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());
}
}
3、基于Apache Shiro权限管理Case -- 密码规则校验器
说明:
因为密码是直接取得数据库的密码,所以想要保证用户传入的密码能和我的密码能够匹配,需要创建自定义的密码校验规则。
具体:通过重写CredentialsMatcher 类,继承SimpleCredentialsMatcher类,重新定义 密码校验规则的重写
/**
* @ClassName CredentialMatcher 密码校验规则的重写
* @Description TODO
* @Author wushaopei
* @Date 2019/9/24 10:07
* @Version 1.0
*/
public class CredentialMatcher extends SimpleCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken token1 = (UsernamePasswordToken) token;
String passsword = new String(token1.getPassword());
String dbPassword = (String) info.getCredentials();
return this.equals(passsword,dbPassword);
}
}
4、基于Apache Shiro权限管理Case -- Shiro配置
创建 ShiroConfiguration 类 ,该类是 shiro 的配置类
@Configuration
public class ShiroConfiguration {
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
bean.setSecurityManager(manager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
bean.setLoginUrl("/login");
// 登录成功后要跳转的链接
bean.setSuccessUrl("/index");
// 未授权界面;
bean.setUnauthorizedUrl("/unauthorized");
// 拦截器.
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/index","authc");
filterChainDefinitionMap.put("/login","anon");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
//配置核心安全事务管理器
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(authRealm);
return securityManager;
}
//配置自定义的权限登录器
@Bean("authRealm")
public AuthRealm authRealm(@Qualifier("credentialMatcher")CredentialMatcher matcher){
AuthRealm authRealm = new AuthRealm();
authRealm.setCredentialsMatcher(matcher); //给出自定密码比较器
return authRealm; //返回自定Realm
}
//配置自定义的密码比较器
@Bean("credentialMatcher") //生成 密码校验规则的Bean
public CredentialMatcher credentialMatcher(){
return new CredentialMatcher(); //返回密码校验规则实例
}
/**
* 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持; Controller才能使用@RequiresPermissions
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager")SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
}
5、定义请求接口:包括首页、登录页、异常页面
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping("/index")
public String index(){
return "index";
}
@RequestMapping("/loginUser")
public String loginUser(@RequestParam("username")String username,
@RequestParam("password") String password,
HttpSession session){
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
User principal = (User) subject.getPrincipal();
session.setAttribute("user",principal);
return "index";
}catch (Exception e){
return "login";
}
}
6、定义jsp 页面,包括index.jsp、login.jsp、
jsp 相关配置:在 yml 文件中,申明jsp的前缀、后缀
spring:
mvc: ## jsp ##
view:
prefix: /pages/
suffix: .jsp
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>欢迎登录</h1>
<form action="/loginUser" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit" value="提交"><br>
</form>
</body>
</html>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<h1>欢迎登录, ${user.username}</h1>
</body>
</html>
接口登录测试:

提交,认证通过

7、基于Apache Shiro权限管理Case -- Shiro配置 登录拦截
shiro 过滤器添加配置:
filterChainDefinitionMap.put("/loginUser","anon"); //登陸不用登陸認證
filterChainDefinitionMap.put("/**","user"); //其他接口要驗證是否登陸過用戶
测试:

除了登录功能不需要进行认证外,其他接口请求都需要进行用户是否登录的认证.
8、基于Apache Shiro权限管理Case -- Shiro配置 角色认证
在 ShiroConfiguration.java 进行配置:
filterChainDefinitionMap.put("/admin","roles[admin]"); //授权过程中认证角色为admin的用户才可以访问
在AuthRealm.java进行配置:
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 授权要先从 session 取出对象来
User user = (User) principals.fromRealm(this.getClass().getName()).iterator().next();
List<String> permissionList = new ArrayList<>();
List<String> roleNameList = new ArrayList<>(); //角色授权拦截。。。。。。。
Set<Role> roleSet = user.getRoles();
if (CollectionUtils.isNotEmpty(roleSet)){
for (Role role : roleSet) {
roleNameList.add(role.getName()); //角色授权拦截。。。。。。。。
Set<Permission> permissionSet = role.getPermissionSet();
if (CollectionUtils.isNotEmpty(permissionSet)){
for (Permission permission : permissionSet) {
permissionList.add(permission.getName());
}
}
}
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(permissionList);
simpleAuthorizationInfo.addRoles(roleNameList); //角色授权拦截。。。。。。
return simpleAuthorizationInfo;
}
8、基于Apache Shiro权限管理Case -- Shiro配置 permission 认证
说明:具有指定 permission 功能的permission才可以访问,如有edit时,才可以访问,否则不能访问
在ShiroConfiguration.java类中 shiroFilter 方法中配置:
filterChainDefinitionMap.put("/edit","perms[edit]");//具有edit 的pemission 才可以访问
快速搭建Spring Boot + Apache Shiro 环境的更多相关文章
- 基于 intellij IDEA 快速搭建Spring Boot项目
在<一步步搭建 Spring Boot maven 框架的工程>一文中,已经介绍了如何使用Eclipse快速搭建Spring Boot项目.由于最近将开发工具由Eclipse ...
- 快速搭建Spring Boot项目
Spring boot是Spring推出的一个轻量化web框架,主要解决了Spring对于小型项目饱受诟病的配置和开发速度问题. Spring Boot 包含的特性如下: 创建可以独立运行的 Spri ...
- Spring boot入门(一):快速搭建Spring boot项目
(一)Spring boot介绍 本部分摘自:https://www.zhihu.com/question/64671972/answer/223383505 Spring Boot是由Pivotal ...
- 构建微服务:快速搭建Spring Boot项目
Spring Boot简介: Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员 ...
- SpringBoot学习(一)—— idea 快速搭建 Spring boot 框架
简介 优点 Spring Boot 可以以jar包的形式独立运行,运行一个Spring Boot 项目只需要通过 java -jar xx.jar 来运行. Spring Boot 可以选择内嵌Tom ...
- cas 3.5.3服务器搭建+spring boot集成+shiro模拟登录(不修改现有shiro认证架构)
因为现有系统外部接入需要,需要支持三方单点登录.由于系统本身已经是微服务架构,由多个业务独立的子系统组成,所以有自己的用户认证微服务(不是cas,我们基础设施已经够多了,现在能不增加就不增加).但是因 ...
- [转载]快速搭建Spring MVC 4开发环境
(一)工作环境准备: JDK 1.7 Eclipse Kepler Apache Tomcat 8.0 (二)在Eclipse中新建Maven工程,在Archetype类型中,选择“maven-arc ...
- 使用idea搭建Spring boot开发初始环境
准备工作 将以下代码加入idea的live template,命名为springbootStartup <parent> <groupId>org.springframewor ...
- 使用IDEA搭建Spring Boot入门项目
简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置 ...
随机推荐
- python学习之组成字符串的两种方式
第一种就是加法,比如 a ='张三' b = '李四' 那么print c =a+b 例如之前提到的 或者数值转换成字符串的 num = 100 str(num) 其他参照表格中的转换即可 2.组成 ...
- js函数传递参数的方式------传值与传递指针
原则: 1. 基本类型:传值 2. 对象:传递指针 应用场景之一: 用jq选择器获取某个div后(例如:element),准备进行某些修改,之后添加到页面中去. 采取例一的方式,append后发现修改 ...
- Coda docs
a doc,反过来就是coda,有点冷. 对我而言,在线文档的好处在于在线数据库.存放代码等. Quip能高亮代码,但有时墙内不香,害得我用APP查看记录.Slite能高亮代码,但表格功能弱.号称al ...
- Kitty Cloud(HTTP_RPC)的全局异常处理
项目地址 https://github.com/yinjihuan/kitty-cloud 异常处理不用我讲,大家都清楚.单独的异常处理太繁琐,全局异常处理可以在一个应用中统一进行异常的处理,非常方便 ...
- 在ef core中使用postgres数据库的全文检索功能实战之中文支持
前言 有关通用的postgres数据库全文检索在ef core中的使用方法,参见我的上一篇文章. 本文实践了zhparser中文插件进行全文检索. 准备工作 安装插件,最方便的方法是直接使用安装好插件 ...
- Java多线程带返回值的Callable接口
Java多线程带返回值的Callable接口 在面试的时候,有时候是不是会遇到面试会问你,Java中实现多线程的方式有几种?你知道吗?你知道Java中有可以返回值的线程吗?在具体的用法你知道吗?如果两 ...
- QQ恢复解散后的群聊或删除后的好友的方法
今天有一个群被一个管理员乱踢人,之后将群解散. 事后几分钟我在想有没有什么方法可以重新恢复的方法,之后进入了QQ的官网进行查找. 本来以为没希望了,但是奇迹发生了. 原来真的可以恢复! 恢复的详情: ...
- Django分页之应用案例
项目文件: models.py(建表) from django.db import models # Create your models here. class Book(models.Model) ...
- oracle 查询表及字段结构
select --*tcl.column_name,cc.comments col_comments,data_type,case data_type when 'NUMBER' then '('|| ...
- ShoneSharp语言(S#)的设计和使用介绍系列(10)— 富家子弟“语句“不炫富
ShoneSharp语言(S#)的设计和使用介绍 系列(10)— 富家子弟“语句“不炫富 作者:Shone 声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/Sho ...