快速搭建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 才可以访问
最新文章
- PostgreSQL和MYsql的对比
- slf4i + logback 配置
- JAVA开发-我的第一个webScan扫描器
- selenium+python笔记6
- IOS开发-UI基础-视图
- Hi java新特性
- NGUI无法按住鼠标按住时无法监听OnHover事件
- 【技术贴】jsp出现getOutputStream() has already been calle
- JavaScript注意事项
- javascript中的同源策略
- WCF技术剖析之十七:消息(Message)详解(上篇)
- 使用SetUnhandledExceptionFilter转储程序崩溃时内存DMP注意事项
- Jquery 时间格式化
- MFC控件(8):command button与syslink control
- #DP# ----- OpenJudge山区建小学
- b.控制结构
- 【Android Studio安装部署系列】二十二、Android studio自动生成set、get方法
- openwrt如何关掉防火墙?
- [转自知乎] 从github上下载单个文件夹
- chmod 777 修改权限
热门文章
- NetCore项目实战篇05---添加Ocelot网关并集成identity server4认证
- Qt和JS的交互
- 【Spark】必须要用CDH版本的Spark?那你是不是需要重新编译?
- vue 如何实现 Input 输入框模糊查询方法
- [whu1564]后缀数组
- POI 导入excel数据自动封装成model对象--代码
- C# 数据操作系列 - 7. EF Core 导航属性配置
- Spring学习笔记(三):面向切面的Spring
- Docker镜像下载及加速器设置
- C# winform DataGridView 绑定数据的的几种方法