【超详细】SpringBoot与Shiro整合-权限管理实战实操(附源码地址)
一、搭建SpringBoot开发环境
1. 安装好开发软件和Maven等
开发工具:Spring Tool Suite(TST)
Maven :3.3.9
jdk:1.8
2. 创建springboot项目(此功能需要联网)
3. 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 http://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.1.3.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.vae</groupId><artifactId>shiro</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-shiro</name><description>Demo project for Spring Boot</description><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><!-- thymeleaf依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- Web支持:Spring web开发支持,servlet相关程序等 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
4. 编写测试Controller层
package com.vae.user.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@Controller
public class UserController {@RequestMapping("/hi")public String hi(){return "hi shiro";}
}
5. 启动spring boot启动类
package com.vae;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/*** springboot启动类* @author vae**/
@SpringBootApplication
public class SpringbootShiroApplication {public static void main(String[] args) {SpringApplication.run(SpringbootShiroApplication.class, args);}
}
6. 浏览器访问http://localhost:8080/hi 测试
二、使用Thymeleaf页面模板
1. pom.xml 文件中添加 thymeleaf依赖
<!-- thymeleaf依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2. 在UserController中编写测试thymeleaf的方法
下面写了两种方式,第一种比较直观和优雅,第二种相对普遍且代码较少,且迎合从struts2
/*** 方式一:返回ModelAndView*/@RequestMapping("/index")public ModelAndView index() {ModelAndView view = new ModelAndView();// 设置跳转的视图 默认映射到 src/main/resources/templates/{viewName}.htmlview.setViewName("index");// 设置属性view.addObject("title", "我的templates页面");view.addObject("desc", "欢迎进入我的csdn博客");Author author = new Author();author.setAge(18);author.setEmail("xhw_vae@163.com");author.setName("way");view.addObject("author", author);return view;}/*** 方式二:返回String* 注意:此方式不可以使用@RestController,@RestController 等价* 于 @Controller 加上 @ResponseBody,@ResponseBody表示* 该方法的返回不会被解析为跳转, 而是直接写入http响应正文。*/@RequestMapping("/index1")public String index1(HttpServletRequest request) {// TODO 与上面的写法不同,但是结果一致。// 设置属性request.setAttribute("title", "我的templates页面");request.setAttribute("desc", "欢迎进入我的csdn博客");Author author = new Author();author.setAge(18);author.setEmail("xhw_vae@163.com");author.setName("way");request.setAttribute("author", author);// 返回的 index 则会映射到 src/main/resources/templates/index.htmlreturn "index";}class Author {private int age;private String name;private String email;// 省略 get set }
3. 在resources/templates文件夹下新建index.html模板文件
可以看到 thymeleaf
是通过在标签中添加额外属性动态绑定数据的
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><!-- 可以看到 thymeleaf 是通过在标签里添加额外属性来绑定动态数据的 --><title th:text="${title}">Title</title><!-- 在/resources/static/js目录下创建一个hello.js 用如下语法依赖即可--><script type="text/javascript" th:src="@{/js/hello.js}"></script>
</head>
<body><h1 th:text="${desc}">Hello World</h1><h2>=====作者信息=====</h2><p th:text="${author?.name}"></p><p th:text="${author?.age}"></p><p th:text="${author?.email}"></p>
</body>
</html>
4. 浏览器访问http://localhost:8080/index测试
三、shiro认证-ShiroConfig配置类
1. shiro的核心API
Subject:用户主体(关联SecurityManager,把操作交给SecurityManager)
SecurityManager:安全管理器(关联Realm)
Realm:shiro连接数据库的桥梁
2. Spring整合shiro
(1)添加shiro和spring整合依赖
<!-- shiro和spring整合依赖 --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency>
(2)自定义Realm类
package com.vae.shiro;import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;public class UserRealm extends AuthorizingRealm{/*** 授权逻辑*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {System.out.println("执行授权逻辑");return null;}/*** 认证逻辑*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {System.out.println("执行认证逻辑");return null;}}
(3)编写shiro配置类(基本结构)
package com.vae.shiro;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** shiro的配置类* @author Administrator**/
@Configuration
public class ShiroConfig {/*** 创建ShiroFilterFactoryBean*/@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
// 设置安全管理器shiroFilterFactoryBean.setSecurityManager(securityManager);return shiroFilterFactoryBean;}/*** 创建DefaultWebSecurityManager*/@Bean(name="securityManager")public DefaultWebSecurityManager getdefaultDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();//关联RealmsecurityManager.setRealm(userRealm);return securityManager;}/*** 创建Realm*/@Bean(name="userRealm")public UserRealm getRealm(){return new UserRealm();}
}
四、shiro认证-使用shiro过滤器实现认证资源拦截
1. 在templates下新建user/add.html和user/update.html
/user/add.html
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>用户新增页面</title>
</head>
<body><h1>用户新增页面</h1>
</body>
</html>
/user/update.html
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>用户修改页面</title>
</head>
<body><h1>用户修改页面</h1>
</body>
</html>
2. 在UserController中编写访问这两个页面的方法
@RequestMapping("/add")public String add() {return "user/add";}@RequestMapping("/update")public String update() {return "user/update";}
3. 在index.html页面中添加这两个页面的超链接
进入用户新增页面:<a href="add">用户新增</a>
进入用户更新页面:<a href="update">用户更新</a>
重启测试http://localhost:8080/index可访问。
4. 修改ShiroConfig类的getShiroFilterFactoryBean方法中添加拦截
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();//设置安全管理器shiroFilterFactoryBean.setSecurityManager(securityManager);/** 添加shiro内置过滤器,实现权限相关的url拦截* 常见过滤器:* anon:无需认证(登录)可以访问* authc:必须认证才可以访问* user:如果使用Remember Me的功能,可以直接访问* perms:该资源必须得到资源权限才可以访问* role:该资源必须得到角色权限才可以访问*/Map<String, String> filterMap = new LinkedHashMap<>();filterMap.put("/add", "authc");filterMap.put("/update", "authc");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);return shiroFilterFactoryBean;}
拦截之后重启,再次访问发现:试图访问add或者update,会自动跳转到login.jsp页面 ,此时没有login.jsp所以会出现404的错误,继续操作后面步骤
Filter | 解释 |
---|---|
anon | 无参,开放权限,可以理解为匿名用户或游客 |
authc | 无参,需要认证 |
logout | 无参,注销,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的 url |
authcBasic | 无参,表示 httpBasic 认证 |
user | 无参,表示必须存在用户,当登入操作时不做检查 |
ssl | 无参,表示安全的URL请求,协议为 https |
perms[user] | 参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms[“user, admin”],当有多个参数时必须每个参数都通过才算通过 |
roles[admin] | 参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles[“admin,user”],当有多个参数时必须每个参数都通过才算通过 |
rest[user] | 根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等 |
port[8081] | 当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数 |
5. 修改拦截后跳转的页面
(1)在templates下新增login.html页面
(2)在ShiroConfig中shiroFilterFactoryBean方法中修改拦截后跳转的页面
//修改跳转的登录页面,不加此项就会跳转到login.jsp页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
(3)在UserController中添加toLogin方法
@RequestMapping("/toLogin")public String toLogin() {return "login";}
测试,当没有权限跳转到该login.html页面
6. 用通配符拦截
filterMap.put("/index", "anon");
filterMap.put("/*", "authc");
五、shiro认证-实现用户登录功能
1. 修改完善之前的login.html页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body><h1>登录页面</h1> <span style="color:red" th:text="${msg}"></span><form method="post" action="login"><input type="text" name="username" /><br><input type="password" name="password"><br><input type="submit" name="submit" value="登录"></form>
</body>
</html>
2. 在UserController中编写/login请求,编写登录的处理逻辑
@RequestMapping("/login")public String login(String username, String password, Model model) {//使用shiro编写认证操作//获取SubjectSubject subject = SecurityUtils.getSubject();//封装用户数据UsernamePasswordToken token = new UsernamePasswordToken(username, password);//执行登录方法try {//只要执行login方法,就会去执行UserRealm中的认证逻辑subject.login(token);//如果没有异常,代表登录成功//跳转到textThymeleaf页面,代表主页return "redirect:/index";} catch (UnknownAccountException e) {logger.info(username + "用户名不存在");//登录失败model.addAttribute("msg", "用户名不存在");return "login";} catch (IncorrectCredentialsException e) {logger.info(username + "密码错误");model.addAttribute("msg", "密码错误");return "login";}}
测试发现,并没有进入/login请求,是因为之前写的拦截器(/*)拦截了所有请求,再对/login请求放行,加入代码:
filterMap.put("/login", "anon");
重启测试,发现执行了认证逻辑,返回了用户名不存在异常。
3. 在UserRealm中编写shiro认证逻辑
/*** 认证逻辑*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {System.out.println("执行认证逻辑");//先写模拟数据进行验证,下一步再连接数据库,假设数据库的用户名和密码如下String dbusername = "vae";String dbpassword = "123456";//编写shiro判断逻辑,判断用户名和密码//1. 判断用户名UsernamePasswordToken token = (UsernamePasswordToken) arg0;if (!token.getUsername().equals(dbusername)) {//用户名不存在shiro底层会抛出UnknownAccountExceptionreturn null;}//2. 判断密码,参数1:需要返回给login方法的数据;参数2:数据库密码,shiro会自动判断return new SimpleAuthenticationInfo("", dbpassword, "");}
4. 启动程序,进行测试
六、shiro认证-整合MyBatis完善用户登录
1. 导入Mybatis相关的依赖
<!-- druid德鲁伊连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- Spring的MyBatis启动器 --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.1.1</version></dependency>
2. 配置application.properties(src/main/resources下)
(1)创建数据库及用户表
CREATE DATABASE `shiro-demo` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci'CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',`username` varchar(36) NOT NULL DEFAULT '' COMMENT '用户名',`password` varchar(36) NOT NULL DEFAULT '' COMMENT '密码',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
(2)配置application.properties
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro-demo?characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root# 连接池配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource#mybatis mapper扫描位置
mybatis.mapper-locations=mapper/*.xml
3. 编写User实体类
package com.vae.user.entity;public class User {private Integer id;private String username;private String password;//省略get和set方法
}
4. 编写接口UserDao.java
package com.vae.user.dao;import com.vae.user.entity.User;public interface UserDao {/*** 根据username查找用户信息* @param username* @return*/User findByUsername(String username);
}
5. 编写UserMapper.xml映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 此处指向dao接口 -->
<mapper namespace="com.vae.shiro.user.dao.UserDao"><!-- 此处指向实体类 --><resultMap id="BaseResultMap" type="com.vae.shiro.user.entity.User"><id column="id" jdbcType="INTEGER" property="id" /><result column="username" jdbcType="VARCHAR" property="username" /><result column="password" jdbcType="VARCHAR" property="password" /></resultMap><sql id="Base_Column_List">id, username, password</sql><!-- 根据username查找用户信息 --><select id="findByUsername" parameterType="java.lang.String" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from userwhere username= #{username,jdbcType=VARCHAR}</select>
</mapper>
6. 编写UserService.java业务接口
package com.vae.user.service;import com.vae.user.entity.User;public interface UserService {/*** 根据username查找用户信息* @param username* @return*/User findByUsername(String username);
}
7. 编写UserServiceImpl.java业务实现类
package com.vae.user.service.impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.vae.user.dao.UserDao;
import com.vae.user.entity.User;
import com.vae.user.service.UserService;@Service
public class UserServiceImpl implements UserService{@Autowiredprivate UserDao userDao;@Overridepublic User findByUsername(String username) {return userDao.findByUsername(username);}}
8. 在Application启动类添加@MapperScan注解,开启Mybatis的Mapper接口扫描
@MapperScan("com.vae.user.dao")
9. 修改UserRealm.java,调用刚编写的业务
@Autowiredprivate UserService userService;/*** 执行认证逻辑 */@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {System.out.println("执行认证逻辑");// 编写shiro判断逻辑,判断用户名和密码System.out.println(arg0.toString());// 1. 判断用户名UsernamePasswordToken token = (UsernamePasswordToken) arg0;User user = userService.findByUsername(token.getUsername());if (user==null) {//用户名不存在return null;//shiro底层会抛出UnknownAccountException}// 2. 判断密码// 参数1:需要返回给login方法的数据;参数2:数据库密码,shiro会自动判断 return new SimpleAuthenticationInfo("", user.getPassword(), "");}
10. 测试
效果相同。
七、Shiro授权-使用Shiro过滤器实现授权页面拦截
1. 在ShiroConfig中添加过滤器
//授权过滤器:授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/add", "perms[user:add]");
filterMap.put("/*", "authc");
Tips:注意要写在/*之前,否则不会拦截
2. 添加设置未授权页面
(1)ShiroConfig中
//修改自动跳转的未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth");
(2)UserController中
@RequestMapping("/unAuth")
public String unAuth() {return "unAuth";
}
(3)添加unAuth.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>未授权页面</title>
</head>
<body><h1>抱歉!您无权限访问!</h1>
</body>
</html>
3. 访问 http://localhost:8080/add 测试
登录认证之后,访问/add页面会提示未授权,而/update可以正常访问。
八、Shiro授权-编写资源授权逻辑
刚才打印的log日志中可以看到,只要访问了需要授权访问的资源,就会执行UserRealm中的doGetAuthenticationInfo()方法,在该方法中给资源进行授权。
/*** 执行授权逻辑*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {System.out.println("执行授权逻辑");//给资源进行授权SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();//添加资源的授权字符串info.addStringPermission("user:add");return info;}
测试查看效果:日志中可以看到执行了该授权逻辑,现在可以访问/add了
九、Shiro授权-关联数据库动态授权
1. 修改数据表
给user表添加perms字段,插入两个测试用户
2. 一系列小修改
(1)User.java:添加perms属性和getter/setter
(2)UserDao.java:
public User findById(Integer id);
(3)UserMapper.xml
<!-- 根据id查找用户信息 --><select id="findById" parameterType="java.lang.Integer" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from userwhere id = #{id,jdbcType=INTEGER}</select>
(4)UserService.java
public User findById(Integer id);
(5)UserServiceImpl.java
@Overridepublic User findById(Integer id) {return userMapper.findById(id);}
(6)给/update添加资源拦截
filterMap.put("/update", "perms[user:update]");
3. 修改UserRealm中的doGetAuthorizationInfo方法
@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {System.out.println("执行授权逻辑");//给资源进行授权SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();/*//添加资源的授权字符串info.addStringPermission("user:add");*///获取当前用户Subject subject=SecurityUtils.getSubject();User user=(User)subject.getPrincipal();//到数据库查询当前登录用户的授权字符串User dbUser=userService.findById(user.getId());//通过当前登录用户id查找的数据库用户info.addStringPermission(dbUser.getPerms()); return info;}
将doGetAuthenticationInfo()方法的返回修改为
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
因为User user=(User)subject.getPrincipal(); 所取得的当前登录用户就是从这里来的
4. 登录不同权限用户进行测试
各自有了各自的权限。
十、ThymeLeaf和shiro标签整合使用
1. 导入thymeleaf对shiro的扩展坐标
<!-- 导入thymeleaf对shiro的扩展坐标 --><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency>
2. 配置ShiroDialect
ShiroConfig中
@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}
3. 在页面上使用shiro标签
<div shiro:hasPermission="user:add">进入用户新增页面:<a href="add">用户新增</a>
</div>
<div shiro:hasPermission="user:update">进入用户更新页面:<a href="update">用户更新</a>
</div>
4. 运行测试
不同权限用户登录,只显示了他有权限看到的内容。
十一.项目完整目录
十二、码云gitee demo代码地址:
shiro-demo: shiro权限系统demo
【超详细】SpringBoot与Shiro整合-权限管理实战实操(附源码地址)相关推荐
- 《SpringBoot与Shiro整合-权限管理实战---从构建到模拟数据库登入》
<SpringBoot与Shiro整合-权限管理实战> ---- 从构建到模拟数据库登入 ---- 点击下载源码 ---- 或者查看? 文章目录 <SpringBoot与Shiro整 ...
- SSH实现进销存(ERP)项目之订单管理模块解析(附源码地址)
项目清单: 1,struts2.hibernate.spring 2,前后台传值使用json 3,数据库使用了Oracle 4,对员工表及采购单表采用了后端分页 5,使用了时间控件 项目结构,MVC模 ...
- Java毕设项目艾灸减肥管理网站计算机(附源码+系统+数据库+LW)
Java毕设项目艾灸减肥管理网站计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Ec ...
- Java毕设项目家政服务公司管理信息计算机(附源码+系统+数据库+LW)
Java毕设项目家政服务公司管理信息计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ ...
- Java毕设项目信管专业毕业生就业管理信息系统计算机(附源码+系统+数据库+LW)
Java毕设项目信管专业毕业生就业管理信息系统计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm ...
- Java毕设项目薪酬福利管理信息系统计算机(附源码+系统+数据库+LW)
Java毕设项目薪酬福利管理信息系统计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ ...
- JAVA计算机毕业设计薪酬福利管理信息系统计算机(附源码、数据库)
JAVA计算机毕业设计薪酬福利管理信息系统计算机(附源码.数据库) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Ecl ...
- Java毕设项目中小学教务管理平台计算机(附源码+系统+数据库+LW)
Java毕设项目中小学教务管理平台计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ E ...
- JAVA计算机毕业设计无人值守台球厅智能管理监控系统(附源码、数据库)
JAVA计算机毕业设计无人值守台球厅智能管理监控系统(附源码.数据库) 目运行 环境项配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ E ...
最新文章
- 第四章第五章 环境搭建和24个命令总结
- 通过.obj生成2d图像_自动生成 凹凸法线灯贴图 插件
- 最人性化的在线作图工具
- 表单验证失败提示方案(自用)
- VC各种情况下的窗口句柄的获取
- 用CSS让DIV上下左右居中的方法
- .NET设计模式(1):1.1 单例模式(Singleton Pattern)
- Web环境下使用Starling出现的问题收集整理
- 科技圈晒开工福利!小米最直接,腾讯最传统,阿里最豪气,你们家的呢?
- rsa算法的java实现,RSA算法的实现——java版
- Python基础-基本语法
- NOIP2020退役记
- Google Daydream实验室:VR设计的三大要素
- 录制软件Bandicam安装教程
- SOLIDWORKS工程图教程:如何进行SOLIDWORKS工程图标注
- x5675相当于e5_2020年最新桌面CPU性能排行天梯图(含至强处理器)
- 淘宝数据魔方技术架构解析【转】
- MAC word2015 插入罗马字母
- 【报告分享】2021年618全面复盘报告-魔镜市场情报(附下载)
- MATLAB 实现电子版的手写签名
热门文章
- 汶川大地震已过十一年,我仍记忆犹新
- SQL查询重复数据和清除重复数据
- Maven-Archetype Catalog
- ArcGis辅助编号功能的插件式实现
- C语言数组的初始化总结
- 靶向嵌合体PEG-ethoxycarbonyl-propanoic/Dodecaethylene glycol
- STM32入门开发--LED模块实现跑马灯
- 电子计算机与多媒体教学教案,《电子计算机与多媒体》教学设计模板3篇
- python3+scrapy+selenium爬取英雄联盟英雄资料
- php odbc连接池,修复︰ 使用连接池的 SQL Server ODBC 连接时设置的连接属性失败