Spring Boot集成Shrio实现权限管理
 
 
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。相比于Spring Security,功能没有那么强大,但现实开发中,我们也不需要那么多的功能。
 
shiro中三个核心组件:Subject, SecurityManager 和 Realms
  • Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
  • SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
  • Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。用户一般会自定义Ream,集成AuthorizingRealm。
 
对于shiro的基本概念介绍如上,本文主要讲Spring Boot如何集成shiro,如何使用。另外该项目使用mybatis-plus操纵数据库,如果有朋友不知道mybatis-plus如何使用,点击链接https://mp.baomidou.com/ 查看如何而是用。项目中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.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com</groupId>
<artifactId>springboot-shrio</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-shrio</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.1.3.RELEASE</version>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
 
在项目中有两个至关重要类需要我们自定义实现,一个是shiroConfig类,一个是CustonRealm类。
ShiroConfig类:
顾名思义就是对shiro的一些配置,相对于之前的xml配置。包括:过滤的文件和权限,密码加密的算法,其用注解等相关功能。

package com.shiro.config;

import com.shiro.realm.CustomRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.HashMap;
import java.util.Map; /**
* @Author IT咸鱼
* @Date 2020/04/26
*/
@Configuration
public class ShiroConfig { private Logger logger = LoggerFactory.getLogger(this.getClass());
//不加这个注解不生效,具体不详
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
} //将自己的验证方式加入容器
@Bean
public CustomRealm myShiroRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
} //权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
logger.info("SecurityManager注册完成");
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
} //Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
logger.info("设置对应的过滤条件和跳转条件");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String,String> map = new HashMap<String,String>();
// 配置不会被拦截的链接 顺序判断,因为前端模板采用了thymeleaf,这里不能直接使用 ("/static/**", "anon")来配置匿名访问,必须配置到每个静态目录
map.put("/css/**", "anon");
map.put("/fonts/**", "anon");
map.put("/img/**", "anon");
map.put("/js/**", "anon");
map.put("/html/**", "anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
map.put("/logout", "logout");
//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
map.put("/**", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
} /**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
 
CustomRealm类:
自定义的CustomRealm继承AuthorizingRealm。并且重写父类中的doGetAuthorizationInfo(权限相关)、doGetAuthenticationInfo(身份认证)这两个方法。

package com.shiro.realm;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.shiro.entity.*;
import com.shiro.service.ITPermissionService;
import com.shiro.service.ITRoleService;
import com.shiro.service.ITUserService;
import com.shiro.service.LoginService;
import org.apache.catalina.authenticator.jaspic.SimpleAuthConfigProvider;
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.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import java.util.List; public class CustomRealm extends AuthorizingRealm { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired
LoginService loginServiceImpl; @Autowired
ITUserService tUserServiceImpl; @Autowired
ITRoleService tRoleServiceImpl; @Autowired
ITPermissionService tPermissionServiceImpl; /**
* 授权
* @param principalCollection
* @return
* @throws AuthenticationException
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) throws AuthenticationException {
logger.info("CustomRealm.doGetAuthorizationInfo,PrincipalCollection={}", principalCollection);
TUser tUser = (TUser)principalCollection.getPrimaryPrincipal();
//添加角色和权限
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
List<TRole> roles = tRoleServiceImpl.getRoleByUserId(tUser.getId());
for (TRole tRole : roles){
authorizationInfo.addRole(tRole.getRoleCode());
List<TPermission> permissions = tPermissionServiceImpl.getPermissionsByRoleId(tRole.getId());
for (TPermission tPermission : permissions){
authorizationInfo.addStringPermission(tPermission.getPermissionCode());
}
}
return authorizationInfo;
} /**
* 用户调用登录接口时调用该方法,校验用户合法性
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
logger.info("CustomRealm.doGetAuthenticationInfo,AuthenticationToken={}", authenticationToken);
if (authenticationToken.getPrincipal() == null){
return null;
}
String userName = authenticationToken.getPrincipal().toString();
QueryWrapper<TUser> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TUser::getUserName, userName);
TUser tUser = tUserServiceImpl.getOne(queryWrapper);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
if (tUser == null){
throw new UnknownAccountException();
}
if (tUser.getStatus() == 0){
throw new LockedAccountException();
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(tUser, tUser.getPassword().toString(), getName());
return simpleAuthenticationInfo;
}
}
 
创建LoginController类,使用postman测试登录接口,获取权限

package com.shiro.controller;

import com.shiro.dto.LoginDto;
import com.shiro.entity.TUser;
import com.shiro.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import java.io.Serializable;
import java.util.Deque; @RestController
public class LoginController { private Logger logger = LoggerFactory.getLogger(this.getClass()); @RequestMapping("/login")
public String login(LoginDto loginDto) {
logger.info("/login, LoginDto={}", loginDto);
//添加用户认证信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
loginDto.getUserName(),
loginDto.getPassword()
);
try {
//进行验证,这里可以捕获异常,然后返回对应信息
subject.login(usernamePasswordToken);
} catch (AuthenticationException e) {
e.printStackTrace();
return "账号或密码错误!";
} catch (AuthorizationException e) {
e.printStackTrace();
return "没有权限";
}
return "login success";
} @RequestMapping("/logout")
public String logout(){
logger.info("/logout");
Subject subject = SecurityUtils.getSubject();
if(null!=subject){
String username = ((TUser) SecurityUtils.getSubject().getPrincipal()).getUserName();
logger.info("username={}", username); }
return "logout success";
}
}
使用postman访问/login接口
 
登录成功后,根据登录成功后的用户权限去操作接口,demo中只有admin和common角色,admin可以增加、删除、更新、读取,common用户只能读取,拿用户管理类TUserController作为例子讲解
package com.shiro.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.shiro.common.ResultHandler;
import com.shiro.entity.TUser;
import com.shiro.service.ITUserService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import java.util.List; /**
* <p>
* 前端控制器
* </p>
*
* @author xieya
* @since 2020-04-28
*/
@RestController
@RequestMapping("/user")
public class TUserController { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired
private ITUserService tUserServiceImpl; @RequiresRoles("admin")//指定需要有admin角色
@RequiresPermissions({"create","update"})//需要有create、update权限
@PostMapping("/save-or-update")
public String saveOrUpdate(@RequestBody TUser tUser){
logger.info("/save-or-update, TUser={}", tUser);
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject();
if (tUser == null){
return ResultHandler.handler(jsonObject, "-1001", "param null");
}
tUserServiceImpl.saveOrUpdate(tUser);
ResultHandler.handler(jsonObject, "0", "Success");
} catch (Exception e) {
logger.info("System Exception,e={}", e);
return ResultHandler.handler(jsonObject, "-9001", "System Exception");
}
return jsonObject.toString();
} @RequiresRoles("admin")
@RequiresPermissions("delete")
@GetMapping("/delete")
public String delete(Long id){
logger.info("/delete, id={}", id);
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject();
if (id == null){
return ResultHandler.handler(jsonObject, "-1001", "param null");
}
tUserServiceImpl.removeById(id);
ResultHandler.handler(jsonObject, "0", "Success");
} catch (Exception e) {
logger.info("System Exception,e={}", e);
return ResultHandler.handler(jsonObject, "-9001", "System Exception");
}
return jsonObject.toString();
} @PostMapping("/retrieve")
public String retrieve(@RequestBody TUser tUser){
logger.info("/retrieve, TUser={}", tUser);
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject();
if (tUser == null){
return ResultHandler.handler(jsonObject, "-1001", "param null");
}
QueryWrapper<TUser> queryWrapper = new QueryWrapper<>();
if (tUser.getId() != null){
queryWrapper.lambda().eq(TUser::getId, tUser.getId());
}
if (!StringUtils.isEmpty(tUser.getUserName())){
queryWrapper.lambda().eq(TUser::getUserName, tUser.getUserName());
}
List<TUser> list = tUserServiceImpl.list(queryWrapper);
ResultHandler.handler(jsonObject, "0", "Success", list);
} catch (Exception e) {
logger.info("System Exception,e={}", e);
return ResultHandler.handler(jsonObject, "-9001", "System Exception");
}
return jsonObject.toString();
}
}

登录之后使用postman去访问“/user/save-or-update”接口

如果在没有登录的情况下访问该接口,就会出现如下错误,在没有登录的请况下,shiro会自动的将接口访问重置到login接口login3

一个简单的小项目,希望能帮上大家

Spring Boot集成Shrio实现权限管理的更多相关文章

  1. spring boot集成shrio用于权限控制

    下面是一个简单的springBoot集成shrio的项目,技术是:spring boot+idea+gradle+shrio+mybatis 1:首先在build.gradle中导入依赖 builds ...

  2. spring boot 2 + shiro 实现权限管理

    Shiro是一个功能强大且易于使用的Java安全框架,主要功能有身份验证.授权.加密和会话管理.看了网上一些文章,下面2篇文章写得不错.Springboot2.0 集成shiro权限管理 Spring ...

  3. Spring Boot 集成Shiro和CAS

    Spring Boot 集成Shiro和CAS 标签: springshirocas 2016-01-17 23:03 35765人阅读 评论(22) 收藏 举报  分类: Spring(42)  版 ...

  4. Spring boot集成RabbitMQ(山东数漫江湖)

    RabbitMQ简介 RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统 MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出 ...

  5. 81. Spring Boot集成JSP疑问【从零开始学Spring Boot】

    [原创文章,转载请注明出处] 针对文章: ()Spring Boot 添加JSP支持[从零开始学Spring Boot] 有网友提了这么一些疑问: 1.Spring Boot使用jsp时,仍旧可以打成 ...

  6. (37)Spring Boot集成EHCache实现缓存机制【从零开始学Spring Boot】

    [本文章是否对你有用以及是否有好的建议,请留言] 写后感:博主写这么一系列文章也不容易啊,请评论支持下. 如果看过我之前(35)的文章这一篇的文章就会很简单,没有什么挑战性了. 那么我们先说说这一篇文 ...

  7. Spring boot集成Rabbit MQ使用初体验

    Spring boot集成Rabbit MQ使用初体验 1.rabbit mq基本特性 首先介绍一下rabbitMQ的几个特性 Asynchronous Messaging Supports mult ...

  8. Spring Boot 集成阿里云 OSS 进行文件存储

    最近因为项目中需要存储很多的图片,不想存储到服务器上,因此就直接选用阿里云的对象服务(Object Storage Service,简称 OSS)来进行存储,本文将介绍 Spring Boot 集成 ...

  9. 【SpringBoot】Spring Boot 集成SwaggerAPI

    Spring Boot 集成SwaggerAPI 文章目录 Spring Boot 集成SwaggerAPI Swagger 添加依赖 配置类 config 控制类 controller 接口测试 页 ...

随机推荐

  1. strip()的正则表达式版本

    题目:写一个函数,它接受一个字符串,做的事情和 strip()字符串方法一样.如果只 传入了要去除的字符串,没有其他参数,那么就从该字符串首尾去除空白字符. 否则,函数第二个参数指定的字符将从该字符串 ...

  2. LCA 学习总结

    怎么说,LCA裸题直接套板子,大家都会做,这样的题没必要看,剩下的题发先LCA只是一个工具就像是搜索一样,只是一个工具而不是一种算法,所以借助这套工具在其图论问题如最长路,数据结构等问题上再去发挥作用 ...

  3. python(random 模块)

     一.Random 模块 注意:random() 是不能直接访问的,需要导入 random 模块,然后通过 random 静态对象调用该方法. 1.random.random() 返回随机生成的一个 ...

  4. 【UEFI】---史上最全的X86平台启动流程分析(软硬结合)

    最近研究了下X86处理器的启动流程分析,相比于常见的ARM来说,X86平台启动流程真的复杂了很多,本次基于项目实际的两个问题入手,研究了包括以下几个部分的内容: 1. 从硬件角度看启动流程 2. 从软 ...

  5. Spring AOP概述

    一.AOP的基本概念: 首先先给出一段比较专业的术语: 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的 ...

  6. 再谈 PHP 未来之路

    前段时间我写过一篇博文<phper:敢问路在何方>,分析了 PHPer 的困境以及 PHP 程序员的学习.进阶突破之路.同时我在知乎上也发过类似的提问.从大家的评论和回答看,大体分为以下几 ...

  7. Java模拟UDP通信

    目录 Java基础:模拟UDP通信 1.一次发送,一次接收 1.1.发送方 1.2.接收方 2.多次发送,多次接收 2.1.发送方 2.2.接收方 3.模拟双方通信 3.1.发送方的线程 3.2.接收 ...

  8. 【Spark】SparkStreaming与flume进行整合

    文章目录 注意事项 SparkStreaming从flume中poll数据 步骤 一.开发flume配置文件 二.启动flume 三.开发sparkStreaming代码 1.创建maven工程,导入 ...

  9. [hdu5372 Segment Game]树状数组

    题意:有两种操作:(1)插入线段,第i次插入的线段左边界为Li,长度为i (2)删除线段,删除第x次插入的线段.每次插入线段之前询问有多少条线段被它覆盖. 思路:由于插入的线段长度是递增的,所以第i次 ...

  10. SpringBoot2.0 @Cacheable 添加超时策略

    SpringBoot2.0 @Cacheable 添加超时策略 逻辑比较简单,废话不多说,直接进入正题: 需求:SpringBoot 利用注解使缓存支持过期时间 (同@Cacheable @Cache ...