Shiro是如何进行权限管理
1、Shiro简介
1.1 什么是Shiro
Apache Shiro 是一个Java的安全框架
Shiro可以非常容易的开发出足够好的应用,其不仅可以使用在JavaSE环境,也可以用在JavaEE环境
Shiro可以完成,认证,授权,加密,会话管理,Web集成 ,缓存等方面
下载地址:http://shiro.apache.org/download.html
1.2 有哪些功能
Authentication:身份认证、登陆、验证用户是不是拥有相应的身份
Authorization:授权,即权限验证,验证某个已认证的用户是不是拥有某个权限,即判断用户能否进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户是否对某个资源有某个权限!
Session Manager:会话管理,即用户登陆后就是第一次会话,在没有退出之前,它的所有信息都在会话中,会话是可以普通的JAVA环境,也可以是Web环境
Cryptography:加密,保护数据的安全性,如密码存储到数据库中,而不是明文存储
Web Support:Web支持,可以非常容易的集成到Web环境
Caching:缓存,比如用户登陆后,用户信息,拥有的角色,权限不必每次去查,这样可以提高效率
Concurrency:Shiro支持多线程应用的并发验证,即:在一个线程中开启另一个线程,能把权限自动的传递过去
Testing:提供测试支持
Run As:允许一个用户假装成另一个用户的身份访问
Remember Me:记住我,这个非常常见的功能,即一次登陆后,下次再来的话可以不用登陆
1.3 Shiro架构
从外部来看Shiro,即应用程序角度来观察如何使用Shiro完成工作;
subject:应用代码直接交互的对象是Subject,也就是说Shiro对外API的核心就是Subject,Subject代表了当前的用户,这个用户不一定是具体的人,与当前应用交互的任何东西都是Subject,如:网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManager;Subject其实是一个门面,SecurityManager才是实际的执行者
SecurityManager:安全管理器,即所有安全有关的操作都会与SercurityManager交互,并且它管理着所有的Subject,可以看出它是Shiro的核心,它对负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色
Realm:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManagery要验证用户身份,那么它需要从Realm获取相应的用户进行笔记,来确定用户身份的合法性;也就是说从Realm得到的用户相应的角色,权限进行验证用户的操作是否能进行,可以把Realm看成DataSource;
1.4 Shiro内部架构
Subject:任何可以与应用交互的”用户“
Subject Manager:相当于Spring MVC中的DispatcherServlet;是Shiro的心脏,所有具体交互管理都通过Security Manager进行控制,它的管理者所有的Subject,且负责进行权限认证,授权,会话,缓存的管理
Authenticator:负责Subject认证,是一个扩展点,可以自定义实现,可以使用认证策略(Authentication Strategy)即什么情况下都算用户通过验证
Authorizer:授权器,即访问控制器,用来决定主题是否拥有权限进行相关操作,即控制着用户访问应用中的那些功能
Realm:可以有一个或者多个的realm,可以认为是安全实体数据源,即用于获取安全实体的,可以用JDBC实现,也可以是内存实现等等,由用户提供;所以一般在应用中都需要实现自己的realm
SessionManager:管理Session生命周期dd饿组件,而Shiro并不仅仅是可以用在Web环境,也可以用在普通的JavaSE环境中
CacheManager:缓存控制器,来管理用户,角色,权限等缓存,因为这些数据基本很少改变,放到缓存后可以提高访问性能
Cryptography:密码模块,Shiro提高了一些常见的加密组件用于密码加密,解密等
2、HelloWorld
1、导入shiro需要的包
<!--shiro整合spring的包--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.5.3</version></dependency>
2、快速开始shiro
import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.*;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.Session;import org.apache.shiro.subject.Subject;import org.apache.shiro.util.Factory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class Quickstart {//使用log来进行日志输出private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);public static void main(String[] args) {// The easiest way to create a Shiro SecurityManager with configured// realms, users, roles and permissions is to use the simple INI config.// We'll do that by using a factory that can ingest a .ini file and// return a SecurityManager instance:// Use the shiro.ini file at the root of the classpath// (file: and url: prefixes load from files and urls respectively)://1、使用工厂模式来获得对象Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//2、三大对象之一:Subject// get the currently executing user// 可以通过方法获取当前用户Subject currentUser = SecurityUtils.getSubject();// Do some stuff with a Session (no need for a web or EJB container!!!)//通过当前对象拿到SessionSession session = currentUser.getSession();session.setAttribute("someKey", "aValue");String value = (String) session.getAttribute("someKey");if (value.equals("aValue")) {log.info("Subject=>的session中someKey对应的值! [" + value + "]");}// let's login the current user so we can check against roles and permissions://判断当前用户是否已经验证if (!currentUser.isAuthenticated()) {//如果已经验证,就拿到一个token//token:令牌,去ini文件里面判断是否拥有这个用户UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");//设置记住我token.setRememberMe(true);try {//执行了登录操作currentUser.login(token);} catch (UnknownAccountException uae) {//未知账户异常log.info("There is no user with username of " + token.getPrincipal());} catch (IncorrectCredentialsException ice) {//密码错误异常log.info("Password for account " + token.getPrincipal() + " was incorrect!");} catch (LockedAccountException lae) {//用户锁定异常log.info("The account for username " + token.getPrincipal() + " is locked. " +"Please contact your administrator to unlock it.");}// ... catch more exceptions here (maybe custom ones specific to your application?catch (AuthenticationException ae) {//认证异常,上面的异常都是其子类//unexpected condition? error?}}//say who they are://print their identifying principal (in this case, a username)://获取当前用户的一个认证码,用户的很多信息可以存储在这里log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");//test a role://测试用户是否由角色if (currentUser.hasRole("schwartz")) {log.info("May the Schwartz be with you!");} else {log.info("Hello, mere mortal.");}//test a typed permission (not instance-level)//测试用户的权限(粗粒度)//如果求权限不满足 就会报相应的错误if (currentUser.isPermitted("lightsaber:wield")) {log.info("You may use a lightsaber ring. Use it wisely.");} else {log.info("Sorry, lightsaber rings are for schwartz masters only.");}//a (very powerful) Instance Level permission://判断用户是否拥有更多权限(细粒度)if (currentUser.isPermitted("winnebago:drive:eagle5")) {log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +"Here are the keys - have fun!");} else {log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");}//all done - log out!//注销currentUser.logout();//结束System.exit(0);}}
3、shiro的配置文件shiro.ini
[users]# user 'root' with password 'secret' and the 'admin' roleroot = secret, admin# user 'guest' with the password 'guest' and the 'guest' roleguest = guest, guest# user 'presidentskroob' with password '12345' ("That's the same combination on# my luggage!!!" ;)), and role 'president'presidentskroob = 12345, president# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'darkhelmet = ludicrousspeed, darklord, schwartz# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'lonestarr = vespa, goodguy, schwartz# -----------------------------------------------------------------------------# Roles with assigned permissions## Each line conforms to the format defined in the# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc# -----------------------------------------------------------------------------[roles]# 'admin' role has all permissions, indicated by the wildcard '*'admin = *# The 'schwartz' role can do anything (*) with any lightsaber:schwartz = lightsaber:*# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with# license plate 'eagle5' (instance specific id)goodguy = winnebago:drive:eagle5
4、log4fj的配置文件
log4j.rootLogger=INFO, stdoutlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n# General Apache librarieslog4j.logger.org.apache=WARN# Springlog4j.logger.org.springframework=WARN# Default Shiro logginglog4j.logger.org.apache.shiro=INFO# Disable verbose logginglog4j.logger.org.apache.shiro.util.ThreadContext=WARNlog4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
3、SpringBoot集成Shiro
1、导入整合的包
<!--thymeleaf整合shiro--><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency><!--lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version><scope>provided</scope></dependency><!-- mybatis-spring-boot-starter --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.0</version></dependency><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--druid数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.12</version></dependency><!--shiro整合spring的包--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.5.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
2、shiro的配置类
package com.wei.config;import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;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;import java.util.LinkedHashMap;import java.util.Map;@Configurationpublic class ShiroConfig {//三大要素//ShiroFilterFactoryBean:3@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("webSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);//添加shiro的内置过滤器/*anon:无需认证即可访问authc:必须认证才可访问user:必须拥有记住我功能才能用perms:拥有对某个资源的权限才能访问role:拥有某个角色权限才能访问*///登录拦截Map<String, String> filterMap = new LinkedHashMap<>();//第一个参数是请求,第二个是设置的权限filterMap.put("/add","authc");filterMap.put("/update","authc");//授权//user用户拥有add权限才能访问 /add请求//正常情况下没有授权,会跳到授权页面filterMap.put("/add","perms[user:add]");filterMap.put("/update","perms[user:update]");bean.setFilterChainDefinitionMap(filterMap);//设置登录的请求bean.setLoginUrl("toLogin");//设置未授权页面bean.setUnauthorizedUrl("/noauth");return bean;}//DefaultWebSecurityManager:2@Bean(name = "webSecurityManager")public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();//关联我们的userRealmwebSecurityManager.setRealm(userRealm);return webSecurityManager;}//创建Realm对象,需要自定义 :1//将自己写的realm对象 交给spring容器托管@Beanpublic UserRealm userRealm(){return new UserRealm();}//整合ShiroDialect:Shiro方言 用来整合shiro-thymeleaf@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}}
3、自定义领域(Realm)
package com.wei.config;import com.wei.entity.User;import com.wei.service.UserService;import org.apache.shiro.SecurityUtils;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.session.Session;import org.apache.shiro.subject.PrincipalCollection;import org.apache.shiro.subject.Subject;import org.springframework.beans.factory.annotation.Autowired;//自定义的Realm UserRealm//要实现自定义的 只需要继承AuthorizingRealm 即可public class UserRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权doGetAuthorizationInfo方法");//进入一些被拦截的页面 就会执行授权方法//给用户赋予权限的对象SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();//给用户添加权限//info.addStringPermission("user:add");//拿到当前登录的对象Subject subject = SecurityUtils.getSubject();User currentUser = (User) subject.getPrincipal();//这里取得是59行存得user得值//没有权限直接返回空 显示自己的没有权限界面if (currentUser.getPerms()==null){return null;}//设置当前用户的权限info.addStringPermission(currentUser.getPerms());//要返回赋予的权限对象return info;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了认证doGetAuthenticationInfo方法");//伪造用户名和密码 真的要再数据库中去取// String name = "root";// String password = "123";//将认证的token转化成存储用户信息的token 方便进行信息的判断UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;//连接真实数据库User user = userService.queryByName(token.getUsername());if (user==null){return null;//没有这个人报错}Subject currentUser = SecurityUtils.getSubject();Session session = currentUser.getSession();session.setAttribute("loginUser",user);//shiro也有一些加密方式:MD5 MD5盐值加密 SHA等 但是如果没有加密 密码正确一样可以访问//这个和SpringSecurity有一点区别//密码认证shiro来做,交给程序员操作可能会导致密码泄露return new SimpleAuthenticationInfo(user,user.getPwd(),"");}}
4、控制器
package com.wei.controller;import com.sun.org.apache.regexp.internal.RE;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.IncorrectCredentialsException;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.subject.Subject;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class MyController {@RequestMapping({"/","/index"})public String toIndex(Model model){model.addAttribute("msg","hello,shiro");return "index";}@RequestMapping("/add")public String toAdd(){return "user/add";}@RequestMapping("/update")public String toUpdate(){return "user/update";}@RequestMapping("/toLogin")public String toLogin(){return "login";}@RequestMapping("/login")public String login(String username,String password,Model model){//获取当前的用户Subject subject = SecurityUtils.getSubject();//封装用户的登录数据 把账号密码放在一个令牌中加密UsernamePasswordToken token = new UsernamePasswordToken(username, password);//使用令牌登录,如果没有异常就说明oktry {subject.login(token);return "index";} catch (UnknownAccountException e) {model.addAttribute("msg","用户名错误");return "login";}catch (IncorrectCredentialsException e){model.addAttribute("msg","密码错误");return "login";}}@RequestMapping("/noauth")@ResponseBodypublic String noauth(){return "没有授权,无法登录";}@RequestMapping("/logout")public String logout(Subject subject){subject.logout();return "index";}}
5、实体类
package com.wei.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@NoArgsConstructor@AllArgsConstructorpublic class User {private int id;private String name;private String pwd;private String perms;}
6、mapper接口
package com.wei.mapper;import com.wei.entity.User;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;@Repository@Mapperpublic interface UserMapper {public User queryByName(@Param("name") String name);}
实现类
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.wei.mapper.UserMapper"><select id="queryByName" parameterType="string" resultType="com.wei.entity.User">select * from user where name = #{name};</select></mapper>
7、Service
package com.wei.service;import com.wei.entity.User;import org.apache.ibatis.annotations.Param;public interface UserService {public User queryByName(String name);}
实现类
package com.wei.service;import com.wei.entity.User;import com.wei.mapper.UserMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic User queryByName(String name) {User user = userMapper.queryByName(name);return user;}}
8、前端模板
login.html
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>登录页面</h1><form th:action="@{/login}" method="post"><p>用户名:</p><input type="text" name="username"><p>密码:</p><input type="password" name="password"><br><input type="submit" value="提交"></form><p th:text="${msg}" style="color: red"></p></body></html>
index.html
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org"xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>首页</h1><p th:text="${msg}"></p><div shiro:hasPermission="user:add"><a th:href="@{/add}"> 去添加页面 </a></div><br><div shiro:hasPermission="user:update"><a th:href="@{/update}"> 去修改页面 </a></div><br></div><div th:if="${session.loginUser == null}"><a th:href="@{/toLogin}">登录</a></div><div th:if="${session.loginUser != null}"><a>已登录</a><a th:href="@{/logout}">注销</a></div></body></html>
add.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>添加页面</h1></body></html>
update.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>修改页面
</h1>
</body>
</html>
9、Mybaties整合Springboot
#设置mybatis
mybatis.type-aliases-package=com.wei.entity
mybatis.mapper-locations=classpath:mapper/*.xml
10、数据源配置
spring:datasource:username: rootpassword: root#serverTimezone=UTC解决时区的报错url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource#Spring Boot 默认是不注入这些属性值的,需要自己绑定#druid 数据源专有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4jfilters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20useGlobalDataSourceStat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
Shiro是如何进行权限管理相关推荐
- 一篇搞定 SpringBoot+Mybatis+Shiro 实现多角色权限管理
初衷:我在网上想找整合springboot+mybatis+shiro并且多角色认证的博客,发现找了好久也没有找到想到的,现在自己会了,就打算写个博客分享出去,希望能帮到你. 原创不易,请点赞支持! ...
- shiro、基于url权限管理、超详细
如果需要本篇博客内容的代码!请到我的博客下载中心去下载 https://download.csdn.net/download/qq_36125138/10719559 项目运行图: 权限管理原理知 ...
- shiro最简洁的权限管理(菜单)控制
主要是思想,步骤的话,还是需要分不同的场景,有什么问题及时留言评论即可. 生活中常见的管理系统中,都会有权限的控制,让不同类型的登录用户看到不同的菜单.其实,类似于uasyui这样的前端框架,是有提供 ...
- OA/CRM/ERP 权限管理系统设计 - bg.work办公开源实现
一 .简介 权限管理对有组织结构的系统重要性无需多言,没有权限限制的管理.办公系统将没有可用性可谈.说实话权限管理系统实现起来没有任何门槛,直接几个if else判断就能解决现实需求 ,但是,如果需要 ...
- 简单的jsp权限管理
简单权限管理的控制 权限管理 代码 权限管理 在java项目中经常会遇到用户的权限管理,通常有一种方法通过shiro框架来进行权限管理,现在我要说的是一种简单的权限管理方法,可以在jsp页面中添加ja ...
- 权限管理(shiro框架)
为什么80%的码农都做不了架构师?>>> 1. 权限管理的流程 页面中的菜单和按钮就是我们访问某个功能的入口. 无论是访问菜单或者按钮,要想完成某个业务操作,实际上是需要发送一 ...
- (39.3) Spring Boot Shiro权限管理【从零开始学Spring Boot】
在学习此小节之前您可能还需要学习: (39.1) Spring Boot Shiro权限管理[从零开始学Spring Boot] http://412887952-qq-com.iteye.com/b ...
- shiro权限管理_重量级课程发布~企业权限管理平台(SpringBoot2.0+Shiro+Vue)
历经半个多月的时间,Debug亲自撸的 "企业员工角色权限管理平台"终于完成了.正如字面意思,本课程讲解的是一个真正意义上的.企业级的项目实战,主要介绍了企业级应用系统中后端应用权 ...
- Spring Boot Shiro 权限管理
Spring Boot Shiro 权限管理 标签: springshiro 2016-01-14 23:44 94587人阅读 评论(60) 收藏 举报 本来是打算接着写关于数据库方面,集成MyBa ...
最新文章
- show status用法
- jQuery选择器和选取方法
- 2018-2019-1 20165219 实验三 实时系统
- IDEA 启动报错could not find main class com/intellij/idea/main解决办法
- Kubernetes NetworkPolicy 工作原理浅析
- c语言判断字符串合法标识符,HDU 2024 C语言合法标识符(以及一些关于输入和ctype.h的内容)...
- Unity Transform bug
- string取某个符号后面的的_String.replace 用的不对性能可能差 10 倍,你用对了吗?...
- 8uftp更改上传的网页内容
- wap网页如何跳转到微信一键关注公众号
- 中控考勤仪IFace302多线程操作时无法订阅事件
- 6.5 发散思维能力
- PHP入门需要多久?
- 十种常见的图像标注方法 | 数据标注
- swiper 上滑触发_在绝地求生加入滑铲等战术动作会怎么样?你们想过吗?
- 数独解、多解(数据结构、栈、回溯法)
- 计算机应用基础第四版答案周南岳,计算机应用基础第周南岳win+office期末复习及答案.docx...
- 你会如何给全局对象添加toString()方法
- 用JSTL实现JSP应用程序快速开发
- Abaqus常用小技巧