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. TOP-K Problems

    最小的K个数 直接数组排序,取出前K个.复杂度\(O(nlogn)\). 分治 此题只要求出最小的K个数,并不要求这K个数有序. 我们可以借鉴快排中的partition做法,将比第K个数小的都放前面, ...

  2. 关于SPFA Bellman-Ford Dijkstra Floyd BFS最短路的共同点与区别

    关于模板什么的还有算法的具体介绍 戳我 这里我们只做所有最短路的具体分析. 那么同是求解最短路,这些算法到底有什么区别和联系: 对于BFS来说,他没有松弛操作,他的理论思想是从每一点做树形便利,那么时 ...

  3. Cypress 自动化环境搭建

    1.Cypress 下载: 官网下载,下载后直接解压即可,解压后便可单机 exe 文件打开 Ps:直接打开 exe 是会报错找不到 json文件的,所以还要安装依赖环境 运行 cypress 项目前, ...

  4. mysql-case..when知识点总结

    case...when..有两种语法: 第一种: case  case_value when when_value  then statement_list [when when_value  the ...

  5. 题目分享H 二代目

    题意:有m个限制,每个限制l1,r1,l2,r2四个数,限制了一个长度为n的数第l1到r1位要与第l2到r2相同,保证r1-l1=r2-l2,求在限制下一共有多少种数 分析: 暴力的话肯定是从l1-r ...

  6. aws mysql 开启慢查询日志, 并利用mysqlsla 分析

    1.开启慢查询日志服务 (a) sql 查询配置 # 查看慢日志是否开启,开启为ON show variables like 'slow_query%'; show variables like 'l ...

  7. Python基础03 id

    id id(x)对应变量x所引用对象的内存地址.可以把id(x)看成变量x的身份标识. is 有时在编程中需要与变量的身份标识打交道,但不是通过 id 函数,而是 is 操作符. The operat ...

  8. Python库的安装与查看

    安装库:    Step1:  win+r键打开此页面,并输入cmd     Step2 :键入cmd并点击确定    Step3 :键入语句:“ pip install 库名 “ 回车即可 这样库就 ...

  9. java基础篇 之 foreach探索

    我们看下这段代码: public class Main { public static void main(String[] args) { List list = new ArrayList(); ...

  10. SSM + MYSQL 酒店客房管理系统

    酒店客房管理系统的设计与实现是采用JSP技术,MYSQL数据库进行开发的.系统具有灵活的一体化设计方式,圆满完成了整个系统的全面设计,系统主要采用JSP技术开发,提高系统的运行性能和安全性,并且易于维 ...