介绍:

  Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。(摘自百度百科)

本文使用springboot+mybatisplus+shiro实现数据库动态的管理用户、角色、权限管理,在本文的最后我会提供源码的下载地址,想看到效果的小伙伴可以直接下载运行就ok了

因为shiro的功能比较多,本章只介绍如下几个功能

  1.当用户没有登陆时只能访问登陆界面

  2.当用户登陆成功后,只能访问该用户下仅有的权限

  3.一个用户不能两个人同时在线

一、数据库设计

  本文的数据库表为5个分别是: 用户表、角色表、权限表、用户角色中间表、角色权限中间表,表的结构和数据项目中会提供(sql和redis工具下方的下载地址中都会有)

二、引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<projectxmlns="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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.chaoqi</groupId><artifactId>springboot_mybatisplus</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>springboot_mybatisplus</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version><relativePath/></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--reids--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--添加jsp依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></dependency><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId></dependency><!--SpringBoot - MyBatis 逆向工程--><dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-core</artifactId><version>1.3.2</version></dependency><!--MyBatis 通用 Mapper--><dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring-boot-starter</artifactId><version>1.1.4</version></dependency><!--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-ehcache</artifactId><version>1.4.0</version></dependency><!--shiro+redis缓存插件--><dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>2.4.2.1-RELEASE</version></dependency><!--fastjson阿里巴巴jSON处理器--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.13</version></dependency><!--<dependency>--><!--<groupId>org.springframework.boot</groupId>--><!--<artifactId>spring-boot-starter-security</artifactId>--><!--</dependency>--><!--工具类--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.7</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.2</version><configuration><configurationFile>src/main/resources/generatorConfig.xml</configurationFile><verbose>true</verbose><overwrite>true</overwrite></configuration><executions><execution><id>Generate MyBatis Artifacts</id><goals><goal>generate</goal></goals></execution></executions><dependencies><dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-core</artifactId><version>1.3.2</version></dependency><dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId><version>3.5.0</version></dependency></dependencies></plugin></plugins></build></project>

 三、编辑application.yml

server:port: 8080spring:mvc:view:prefix: /WEB-INF/jsp/suffix: .jspdatasource:url: jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8&useUnicode=true&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverredis:host: localhostport: 6379jedis:pool:max-idle: 8min-idle: 0max-active: 8max-wait: -1timeout: 0mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.chaoqi.springboot_mybatisplus.domain

 四、创建ShiroConfig配置

packagecom.chaoqi.springboot_shiro_redis.config;importcom.chaoqi.springboot_shiro_redis.secutity.KickoutSessionControlFilter;importcom.chaoqi.springboot_shiro_redis.secutity.MyShiroRealm;importorg.apache.shiro.mgt.SecurityManager;importorg.apache.shiro.spring.LifecycleBeanPostProcessor;importorg.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;importorg.apache.shiro.spring.web.ShiroFilterFactoryBean;importorg.apache.shiro.web.mgt.DefaultWebSecurityManager;importorg.apache.shiro.web.session.mgt.DefaultWebSessionManager;importorg.crazycake.shiro.RedisCacheManager;importorg.crazycake.shiro.RedisManager;importorg.crazycake.shiro.RedisSessionDAO;importorg.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjavax.servlet.Filter;importjava.util.LinkedHashMap;importjava.util.Map;@Configurationpublic classShiroConfig {@BeanpublicShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean= newShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);//没有登陆的用户只能访问登陆页面shiroFilterFactoryBean.setLoginUrl("/auth/login");//登录成功后要跳转的链接shiroFilterFactoryBean.setSuccessUrl("/auth/index");//未授权界面; ----这个配置了没卵用,具体原因想深入了解的可以自行百度//shiroFilterFactoryBean.setUnauthorizedUrl("/auth/403");//自定义拦截器Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();//限制同一帐号同时在线的个数。filtersMap.put("kickout", kickoutSessionControlFilter());shiroFilterFactoryBean.setFilters(filtersMap);//权限控制map.Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();filterChainDefinitionMap.put("/css/**", "anon");filterChainDefinitionMap.put("/js/**", "anon");filterChainDefinitionMap.put("/img/**", "anon");filterChainDefinitionMap.put("/auth/login", "anon");filterChainDefinitionMap.put("/auth/logout", "logout");filterChainDefinitionMap.put("/auth/kickout", "anon");filterChainDefinitionMap.put("/**", "authc,kickout");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);returnshiroFilterFactoryBean;}@BeanpublicSecurityManager securityManager() {DefaultWebSecurityManager securityManager= newDefaultWebSecurityManager();//设置realm.
securityManager.setRealm(myShiroRealm());//自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());//自定义session管理 使用redis
securityManager.setSessionManager(sessionManager());returnsecurityManager;}/*** 身份认证realm; (这个需要自己写,账号密码校验;权限等)**@return*/@BeanpublicMyShiroRealm myShiroRealm() {MyShiroRealm myShiroRealm= newMyShiroRealm();returnmyShiroRealm;}/*** cacheManager 缓存 redis实现* 使用的是shiro-redis开源插件**@return*/publicRedisCacheManager cacheManager() {RedisCacheManager redisCacheManager= newRedisCacheManager();redisCacheManager.setRedisManager(redisManager());returnredisCacheManager;}/*** 配置shiro redisManager* 使用的是shiro-redis开源插件**@return*/publicRedisManager redisManager() {RedisManager redisManager= newRedisManager();redisManager.setHost("localhost");redisManager.setPort(6379);redisManager.setExpire(1800);//配置缓存过期时间redisManager.setTimeout(0);//redisManager.setPassword(password);returnredisManager;}/*** Session Manager* 使用的是shiro-redis开源插件*/@BeanpublicDefaultWebSessionManager sessionManager() {DefaultWebSessionManager sessionManager= newDefaultWebSessionManager();sessionManager.setSessionDAO(redisSessionDAO());returnsessionManager;}/*** RedisSessionDAO shiro sessionDao层的实现 通过redis* 使用的是shiro-redis开源插件*/@BeanpublicRedisSessionDAO redisSessionDAO() {RedisSessionDAO redisSessionDAO= newRedisSessionDAO();redisSessionDAO.setRedisManager(redisManager());returnredisSessionDAO;}/*** 限制同一账号登录同时登录人数控制**@return*/@BeanpublicKickoutSessionControlFilter kickoutSessionControlFilter() {KickoutSessionControlFilter kickoutSessionControlFilter= newKickoutSessionControlFilter();kickoutSessionControlFilter.setCacheManager(cacheManager());kickoutSessionControlFilter.setSessionManager(sessionManager());kickoutSessionControlFilter.setKickoutAfter(false);kickoutSessionControlFilter.setMaxSession(1);kickoutSessionControlFilter.setKickoutUrl("/auth/kickout");returnkickoutSessionControlFilter;}/**** 授权所用配置**@return*/@BeanpublicDefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator= newDefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);returndefaultAdvisorAutoProxyCreator;}/**** 使授权注解起作用不如不想配置可以在pom文件中加入* <dependency>*<groupId>org.springframework.boot</groupId>*<artifactId>spring-boot-starter-aop</artifactId>*</dependency>*@paramsecurityManager*@return*/@BeanpublicAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor= newAuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);returnauthorizationAttributeSourceAdvisor;}/*** Shiro生命周期处理器**/@BeanpublicLifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {return newLifecycleBeanPostProcessor();}}

五、自定义Realm

packagecom.chaoqi.springboot_shiro_redis.secutity;importcom.chaoqi.springboot_shiro_redis.service.SysRoleService;importcom.chaoqi.springboot_shiro_redis.service.UserService;importcom.chaoqi.springboot_shiro_redis.dao.domain.SysUser;import org.apache.shiro.authc.*;importorg.apache.shiro.authz.AuthorizationInfo;importorg.apache.shiro.authz.SimpleAuthorizationInfo;importorg.apache.shiro.realm.AuthorizingRealm;importorg.apache.shiro.subject.PrincipalCollection;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;import java.util.*;public class MyShiroRealm extendsAuthorizingRealm {private static org.slf4j.Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);//如果项目中用到了事物,@Autowired注解会使事物失效,可以自己用get方法获取值
@AutowiredprivateSysRoleService roleService;@AutowiredprivateUserService userService;/*** 认证信息.(身份验证) : Authentication 是用来验证用户身份**/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throwsAuthenticationException {logger.info("---------------- 执行 Shiro 凭证认证 ----------------------");UsernamePasswordToken token=(UsernamePasswordToken) authcToken;String name=token.getUsername();String password=String.valueOf(token.getPassword());SysUser user= newSysUser();user.setUserName(name);user.setPassWord(password);//从数据库获取对应用户名密码的用户SysUser userList =userService.getUser(user);if (userList != null) {//用户为禁用状态if (userList.getUserEnable() != 1) {throw newDisabledAccountException();}logger.info("---------------- Shiro 凭证认证成功 ----------------------");SimpleAuthenticationInfo authenticationInfo= newSimpleAuthenticationInfo(userList,//用户userList.getPassWord(), //密码getName()  //realm name
);returnauthenticationInfo;}throw newUnknownAccountException();}/*** 授权*/@OverrideprotectedAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {logger.info("---------------- 执行 Shiro 权限获取 ---------------------");Object principal=principals.getPrimaryPrincipal();SimpleAuthorizationInfo authorizationInfo= newSimpleAuthorizationInfo();if (principal instanceofSysUser) {SysUser userLogin=(SysUser) principal;Set<String> roles =roleService.findRoleNameByUserId(userLogin.getId());authorizationInfo.addRoles(roles);Set<String> permissions =userService.findPermissionsByUserId(userLogin.getId());authorizationInfo.addStringPermissions(permissions);}logger.info("---- 获取到以下权限 ----");logger.info(authorizationInfo.getStringPermissions().toString());logger.info("---------------- Shiro 权限获取成功 ----------------------");returnauthorizationInfo;}}

六、限制并发人数登陆

packagecom.chaoqi.springboot_shiro_redis.secutity;importcom.alibaba.fastjson.JSON;importcom.chaoqi.springboot_shiro_redis.dao.domain.SysUser;importorg.apache.shiro.cache.Cache;importorg.apache.shiro.cache.CacheManager;importorg.apache.shiro.session.Session;importorg.apache.shiro.session.mgt.DefaultSessionKey;importorg.apache.shiro.session.mgt.SessionManager;importorg.apache.shiro.subject.Subject;importorg.apache.shiro.web.filter.AccessControlFilter;importorg.apache.shiro.web.util.WebUtils;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;importjava.io.IOException;importjava.io.PrintWriter;importjava.io.Serializable;importjava.util.Deque;importjava.util.HashMap;importjava.util.LinkedList;importjava.util.Map;public class KickoutSessionControlFilter extendsAccessControlFilter {private String kickoutUrl; //踢出后到的地址private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户private int maxSession = 1; //同一个帐号最大会话数 默认1privateSessionManager sessionManager;private Cache<String, Deque<Serializable>>cache;public voidsetKickoutUrl(String kickoutUrl) {this.kickoutUrl =kickoutUrl;}public void setKickoutAfter(booleankickoutAfter) {this.kickoutAfter =kickoutAfter;}public void setMaxSession(intmaxSession) {this.maxSession =maxSession;}public voidsetSessionManager(SessionManager sessionManager) {this.sessionManager =sessionManager;}//设置Cache的key的前缀public voidsetCacheManager(CacheManager cacheManager) {this.cache = cacheManager.getCache("shiro_redis_cache");}@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throwsException {return false;}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throwsException {Subject subject=getSubject(request, response);if(!subject.isAuthenticated() && !subject.isRemembered()) {//如果没有登录,直接进行之后的流程return true;}Session session=subject.getSession();SysUser user=(SysUser) subject.getPrincipal();String username=user.getUserName();Serializable sessionId=session.getId();//读取缓存   没有就存入Deque<Serializable> deque =cache.get(username);//如果此用户没有session队列,也就是还没有登录过,缓存中没有//就new一个空队列,不然deque对象为空,会报空指针if(deque==null){deque= new LinkedList<Serializable>();}//如果队列里没有此sessionId,且用户没有被踢出;放入队列if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) {//将sessionId存入队列
deque.push(sessionId);//将用户的sessionId队列缓存
cache.put(username, deque);}//如果队列里的sessionId数超出最大会话数,开始踢人while(deque.size() >maxSession) {Serializable kickoutSessionId= null;if(kickoutAfter) { //如果踢出后者kickoutSessionId =deque.removeFirst();//踢出后再更新下缓存队列
cache.put(username, deque);}else { //否则踢出前者kickoutSessionId =deque.removeLast();//踢出后再更新下缓存队列
cache.put(username, deque);}try{//获取被踢出的sessionId的session对象Session kickoutSession = sessionManager.getSession(newDefaultSessionKey(kickoutSessionId));if(kickoutSession != null) {//设置会话的kickout属性表示踢出了kickoutSession.setAttribute("kickout", true);}}catch (Exception e) {//ignore exception
}}//如果被踢出了,直接退出,重定向到踢出后的地址if (session.getAttribute("kickout") != null) {//会话被踢出了try{//退出登录
subject.logout();}catch (Exception e) { //ignore
}saveRequest(request);Map<String, String> resultMap = new HashMap<String, String>();//判断是不是Ajax请求if ("XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"))) {resultMap.put("user_status", "300");resultMap.put("message", "您已经在其他地方登录,请重新登录!");//输出json串
out(response, resultMap);}else{//重定向
WebUtils.issueRedirect(request, response, kickoutUrl);}return false;}return true;}private void out(ServletResponse hresponse, Map<String, String>resultMap)throwsIOException {try{hresponse.setCharacterEncoding("UTF-8");PrintWriter out=hresponse.getWriter();out.println(JSON.toJSONString(resultMap));out.flush();out.close();}catch(Exception e) {System.err.println("KickoutSessionFilter.class 输出JSON异常,可以忽略。");}}
}

七、异常处理类,拦截未授权页面(未授权页面有三种实现方式,我这里使用异常处理)

packagecom.chaoqi.springboot_shiro_redis.exception;importorg.apache.shiro.authz.AuthorizationException;importorg.apache.shiro.authz.UnauthorizedException;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.http.HttpStatus;importorg.springframework.web.bind.annotation.ControllerAdvice;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.ResponseStatus;/*** 全局异常处理类*/@ControllerAdvicepublic classCtrlExceptionHandler {private static Logger logger = LoggerFactory.getLogger(CtrlExceptionHandler.class);//拦截未授权页面@ResponseStatus(value =HttpStatus.FORBIDDEN)@ExceptionHandler(UnauthorizedException.class)publicString handleException(UnauthorizedException e) {logger.debug(e.getMessage());return "403";}@ResponseStatus(value=HttpStatus.FORBIDDEN)@ExceptionHandler(AuthorizationException.class)publicString handleException2(AuthorizationException e) {logger.debug(e.getMessage());return "403";}
}

 八、最后附上logincontroller的代码,调用login就可以调到登陆页面

packagecom.chaoqi.springboot_shiro_redis.web;importcom.chaoqi.springboot_shiro_redis.dao.domain.SysUser;importcom.chaoqi.springboot_shiro_redis.utils.RequestUtils;importorg.apache.commons.lang3.StringUtils;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.authc.AuthenticationException;importorg.apache.shiro.authc.DisabledAccountException;importorg.apache.shiro.authc.UsernamePasswordToken;importorg.apache.shiro.subject.Subject;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestMethod;importjavax.servlet.http.HttpServletRequest;@Controller
@RequestMapping(value= "/auth")public classLoginController {@RequestMapping(value= "/login", method =RequestMethod.POST)publicString submitLogin(String username, String password, HttpServletRequest request) {try{UsernamePasswordToken token= newUsernamePasswordToken(username, password);Subject subject=SecurityUtils.getSubject();subject.login(token);SysUser user=(SysUser) subject.getPrincipal();}catch(DisabledAccountException e) {request.setAttribute("msg", "账户已被禁用");return "login";}catch(AuthenticationException e) {request.setAttribute("msg", "用户名或密码错误");return "login";}//执行到这里说明用户已登录成功return "redirect:/auth/index";}@RequestMapping(value= "/login", method =RequestMethod.GET)publicString loginPage() {return "login";}@RequestMapping(value= "/index", method =RequestMethod.GET)publicString loginSuccessMessage(HttpServletRequest request) {String username= "未登录";SysUser currentLoginUser=RequestUtils.currentLoginUser();if (currentLoginUser != null &&StringUtils.isNotEmpty(currentLoginUser.getUserName())) {username=currentLoginUser.getUserName();}else{return "redirect:/auth/login";}request.setAttribute("username", username);return "index";}//被踢出后跳转的页面@RequestMapping(value = "/kickout", method =RequestMethod.GET)publicString kickOut() {return "kickout";}
}

至此shiro整合完成,源码下载地址为:https://github.com/caicahoqi/ChaoqiIsPrivateLibrary 如果在项目搭建中遇到问题可以在评论区留言,博主看到第一时间会给予回复,谢谢

转载于:https://www.cnblogs.com/caichaoqi/p/8900677.html

springboot+shiro+redis项目整合相关推荐

  1. springboot+shiro+redis+jwt实现多端登录:PC端和移动端同时在线(不同终端可同时在线)

    前言 之前写了篇 springboot+shiro+redis多端登录:单点登录+移动端和PC端同时在线 的文章,但是token用的不是 jwt 而是 sessionID,虽然已经实现了区分pc端和移 ...

  2. 单realm模式下前后端分离实现springboot+shiro+jwt+vue整合

    shiro+jwt实现前后端分离 一.RBAC概念 基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注.在R ...

  3. SpringBoot多模块项目整合Dubbo

    SpringBoot框架整合Dubbo有3种方式: Demo的项目结构: 1.dubbo.xml + @ImportResource注解         (1)导入包: <dependency& ...

  4. Redis入门(3): 数据新类型(BitmapsHyperLogLog,GeoSpatial),Jredis,springboot与redis的整合

    Redis新数据类型 Bitmaps 简介 现代计算机用二进制(位) 作为信息的基础单位, 1个字节等于8位, 例如"abc"字符串是由3个字节组成, 但实际在计算机存储时将其用二 ...

  5. spring boot shiro redis整合基于角色和权限的安全管理-Java编程

    一.概述 本博客主要讲解spring boot整合Apache的shiro框架,实现基于角色的安全访问控制或者基于权限的访问安全控制,其中还使用到分布式缓存redis进行用户认证信息的缓存,减少数据库 ...

  6. 全程配图超清晰的Springboot权限控制后台管理项目实战第二期(Springboot+shiro+mybatis+redis)

    全程配图超清晰的Springboot权限控制后台管理项目实战第二期(Springboot+shiro+mybatis+redis) 众所周知,作为一个后端新手学习者,通过项目来学习,增长项目经验,是一 ...

  7. Docker 部署 SpringBoot 项目整合 Redis 镜像做访问计数Demo

    Docker 部署SpringBoot项目整合 Redis 镜像做访问计数Demo 最终效果如下 大概就几个步骤 1.安装 Docker CE 2.运行 Redis 镜像 3.Java 环境准备 4. ...

  8. 基于springboot+shiro一套可落地实施安全认证框架整合

    前言 俗话说,兵马未动,粮草先行,万丈高楼平地起,一套切实可用的微服务框架是整个项目小组进行后续高效开发的保障,在前期微服务框架构建过程中,大体来说,主要考虑3个点: 技术选项,如果公司业务规模能够提 ...

  9. 视频教程-基于springboot2.x+layui+shiro+redis整合前后端分离的权限管理系统-Java

    基于springboot2.x+layui+shiro+redis整合前后端分离的权限管理系统 拥有八年的Java项目开发经验,擅长Java.vue.SpringBoot.springCloud.sp ...

最新文章

  1. java 源码分析_Java 源代码编译成 Class 文件的过程分析
  2. 中国古代历朝首都一览
  3. python 进程池_python进程池
  4. SpringCloud Greenwich(一)注册中心之nacos、Zuul和 gateway网关配置
  5. electron安装比较慢的方法
  6. 第41课 蜗牛与葡萄树 《小学生C++趣味编程》
  7. 如何用sbt新建akka项目
  8. EasyRecovery如何恢复javascript文件
  9. Playing Atari with Deep Reinforcement Learning 中文 讲解3
  10. 科大讯飞 离线语音识别python_使用python语言调用科大讯飞离线语音合成
  11. BIOS升级之:P5QPL-AM
  12. matlab与计量经济学,matlab与计量经济学
  13. winscp 查看隐藏文件
  14. 搜索引擎原理第三阶段之排名
  15. ORA-01940: cannot drop a user that is currently connected 问题解决
  16. html的盒子随页面动,JavaScript实现跟随鼠标移动的盒子
  17. Latex最后一页文本或参考文献左右对齐(平衡)
  18. 《金字塔原理》读书思维导图
  19. 计算机网络安全包括免疫性吗,【单选题】计算机网络安全的目标不包括A. 保密性 B. 不可否认性 C. 免疫性 D. 完整性...
  20. lol服务器维护2021,lol维护几点结束 2021英雄联盟维护最新公告

热门文章

  1. mysql innodb flus_MySQL参数解析innodb_flush_neighbors
  2. usg6620 查看端口流量_WhatRoute for Mac(互联网流量诊断查询工具)
  3. python 有序字典_(Python基础教程之十七)Python OrderedDict –有序字典
  4. 导入win32_为什么DLL在导入项没有成功解析时会加载失败?
  5. 访问 asp网页 白屏_(02)ASP如何设定主目录和默认文档
  6. Redirecting to binsystemctl start crond.service
  7. idea编译的文件怎么用cmd打开_JAVA学习册|基础语法|cmd输出HelloWorld
  8. html5 元素重叠,javascript – 检查两个或多个DOM元素是否重叠
  9. edittext怎么输入默认内容覆盖_Linux Shell 输入与输出重定向
  10. 云南公务员计算机类岗竞争大吗,2020云南省考难吗?楚雄州历年竞争比、进面分数告诉你...