Apache Shiro

优势特点

它是一个功能强大、灵活的,优秀开源的安全框架。

它可以处理身份验证、授权、企业会话管理和加密。

它易于使用和理解,相比Spring Security入门门槛低。

主要功能

  • 验证用户身份
  • 用户访问权限控制
  • 支持单点登录(SSO)功能
  • 可以响应认证、访问控制,或Session事件
  • 支持提供“Remember Me”服务
  • 。。。

框架体系

Shiro 的整体框架如下图所示:

Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)被 Shiro 框架的开发团队称之为应用安全的四大基石。

它们分别是:

  • Authentication(认证):用户身份识别,通常被称为用户“登录”。
  • Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
  • Session Management(会话管理):特定于用户的会话管理,甚至在非web 应用程序。
  • Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。

除此之外,还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持:

  • Web支持:Shiro 提供的 web 支持 api ,可以很轻松的保护 web 应用程序的安全。
  • 缓存:缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。
  • 并发:Apache Shiro 支持多线程应用程序的并发特性。
  • 测试:支持单元测试和集成测试,确保代码和预想的一样安全。
  • “Run As”:这个功能允许用户假设另一个用户的身份(在许可的前提下)。
  • “Remember Me”:跨 session 记录用户的身份,只有在强制需要时才需要登录。

主要流程

在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager 和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。

  • Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
  • SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
  • Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。

我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

以上描述摘抄自纯洁的微笑博客文章,更多详情可以参考:

Shiro 官网:http://shiro.apache.org/

纯洁的微笑:http://www.ityouknow.com/springboot/2017/06/26/springboot-shiro.html

Shiro 集成

下面就来讲解如何在我们的项目里集成 Shiro 框架。

引入依赖

首先上 maven 仓库查找,当前最新的版本是 1.4.0,我们就用这个版本。

kitty-pom/pom.xml 父POM中添加属性和 dependencyManagement 依赖

<shiro.version>1.4.0</shiro.version>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>

kitty-admin/pom.xml 添加 dependencies 依赖

<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
</dependency>

同理,把后续要用到的几个工具包也导入进来。

<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- commons -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons.codec.version}</version>
</dependency>

添加配置

1. 添加配置类

添加配置类,注入自定义的认证过滤器(OAuth2Filter)和认证器(OAuth2Realm),并添加请求路径拦截配置。

ShiroConfig.java

package com.louis.kitty.boot.config;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map; import javax.servlet.Filter; import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import com.louis.kitty.admin.oauth2.OAuth2Filter;
import com.louis.kitty.admin.oauth2.OAuth2Realm; /**
* Shiro 配置
* @author Louis
* @date Sep 1, 2018
*/
@Configuration
public class ShiroConfig { @Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
// 自定义 OAuth2Filter 过滤器,替代默认的过滤器
Map<String, Filter> filters = new HashMap<>();
filters.put("oauth2", new OAuth2Filter());
shiroFilter.setFilters(filters);
// 访问路径拦截配置,"anon"表示无需验证,未登录也可访问
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/webjars/**", "anon");
// 查看SQL监控(druid)
filterMap.put("/druid/**", "anon");
// 首页和登录页面
filterMap.put("/", "anon");
filterMap.put("/sys/login", "anon");
// swagger
filterMap.put("/swagger-ui.html", "anon");
filterMap.put("/swagger-resources", "anon");
filterMap.put("/v2/api-docs", "anon");
filterMap.put("/webjars/springfox-swagger-ui/**", "anon");
// 其他所有路径交给OAuth2Filter处理
filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
} @Bean
public Realm getShiroRealm(){
OAuth2Realm myShiroRealm = new OAuth2Realm();
return myShiroRealm;
} @Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 注入 Realm 实现类,实现自己的登录逻辑
securityManager.setRealm(getShiroRealm());
return securityManager;
}
}

2. 认证过滤器

拦截除配置成不需认证的请求路径外的请求,都交由这个过滤器处理,负责接收前台带过来的token并封装成对象,如果请求没有携带token,则提示错误。

OAuth2Filter.java

package com.louis.kitty.admin.oauth2;

import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter; import com.alibaba.fastjson.JSONObject;
import com.louis.kitty.common.utils.StringUtils;
import com.louis.kitty.core.http.HttpResult;
import com.louis.kitty.core.http.HttpStatus; /**
* Oauth2过滤器
* @author Louis
* @date Sep 1, 2018
*/
public class OAuth2Filter extends AuthenticatingFilter { @Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
// 获取请求token
String token = getRequestToken((HttpServletRequest) request);
if(StringUtils.isBlank(token)){
return null;
}
return new OAuth2Token(token);
} @Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return false;
} @Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// 获取请求token,如果token不存在,直接返回401
String token = getRequestToken((HttpServletRequest) request);
if(StringUtils.isBlank(token)){
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpResult result = HttpResult.error(HttpStatus.SC_UNAUTHORIZED, "invalid token");
String json = JSONObject.toJSONString(result);
httpResponse.getWriter().print(json);
return false;
}
return executeLogin(request, response);
} @Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setContentType("application/json; charset=utf-8");
try {
// 处理登录失败的异常
Throwable throwable = e.getCause() == null ? e : e.getCause();
HttpResult result = HttpResult.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());
String json = JSONObject.toJSONString(result);
httpResponse.getWriter().print(json);
} catch (IOException e1) {
}
return false;
} /**
* 获取请求的token
*/
private String getRequestToken(HttpServletRequest httpRequest){
// 从header中获取token
String token = httpRequest.getHeader("token");
// 如果header中不存在token,则从参数中获取token
if(StringUtils.isBlank(token)){
token = httpRequest.getParameter("token");
}
return token;
} }

OAuth2Token.java

package com.louis.kitty.admin.oauth2;

import org.apache.shiro.authc.AuthenticationToken;

/**
* 自定义 token 对象
* @author Louis
* @date Sep 1, 2018
*/
public class OAuth2Token implements AuthenticationToken {
private static final long serialVersionUID = 1L; private String token; public OAuth2Token(String token){
this.token = token;
} @Override
public String getPrincipal() {
return token;
} @Override
public Object getCredentials() {
return token;
}
}

3. 逻辑认证器

逻辑认证器是认证和授权的主体逻辑,主要包含两部分。

doGetAuthenticationInfo:实现自己的登录验证逻辑,这里主要是认证 token。

doGetAuthorizationInfo:实现接口授权逻辑,收集权限标识或角色,用来判定接口是否可以访问

OAuth2Realm.java

package com.louis.kitty.admin.oauth2;

import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import com.louis.kitty.admin.model.SysUser;
import com.louis.kitty.admin.model.SysUserToken;
import com.louis.kitty.admin.sevice.SysUserService;
import com.louis.kitty.admin.sevice.SysUserTokenService; /**
* 认证Realm实现
* @author Louis
* @date Sep 1, 2018
*/
@Component
public class OAuth2Realm extends AuthorizingRealm { @Autowired
SysUserService sysUserService;
@Autowired
SysUserTokenService sysUserTokenService; @Override
public boolean supports(AuthenticationToken token) {
return token instanceof OAuth2Token;
} /**
* 授权(接口保护,验证接口调用权限时调用)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SysUser user = (SysUser)principals.getPrimaryPrincipal();
// 用户权限列表,根据用户拥有的权限标识与如 @permission标注的接口对比,决定是否可以调用接口
Set<String> permsSet = sysUserService.findPermissions(user.getUsername());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permsSet);
return info;
} /**
* 认证(登录时调用)
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String token = (String) authenticationToken.getPrincipal();
// 根据accessToken,查询用户token信息
SysUserToken sysUserToken = sysUserTokenService.findByToken(token);
if(sysUserToken == null || sysUserToken.getExpireTime().getTime() < System.currentTimeMillis()){
// token已经失效
throw new IncorrectCredentialsException("token失效,请重新登录");
}
// 查询用户信息
SysUser user = sysUserService.findById(sysUserToken.getUserId());
// 账号被锁定
if(user.getStatus() == 0){
throw new LockedAccountException("账号已被锁定,请联系管理员");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, token, getName());
return info;
}
}

4. 完善登录接口

完善登录逻辑,在用户密码匹配成功之后,创建并保存token,最后将token返回给前台,以后请求带上token。

SysLoginController.java

    /**
* 登录接口
*/
@PostMapping(value = "/sys/login")
public HttpResult login(@RequestBody LoginBean loginBean) throws IOException {
String username = loginBean.getUsername();
String password = loginBean.getPassword(); // 用户信息
SysUser user = sysUserService.findByUserName(username); // 账号不存在、密码错误
if (user == null) {
return HttpResult.error("账号不存在");
} if (!match(user, password)) {
return HttpResult.error("密码不正确");
} // 账号锁定
if (user.getStatus() == 0) {
return HttpResult.error("账号已被锁定,请联系管理员");
} // 生成token,并保存到数据库
SysUserToken data = sysUserTokenService.createToken(user.getUserId());
return HttpResult.ok(data);
} /**
* 验证用户密码
* @param user
* @param password
* @return
*/
public boolean match(SysUser user, String password) {
return user.getPassword().equals(PasswordUtils.encrypte(password, user.getSalt()));
}

SysUserTokenServiceImpl.java,生成并保存token,这里把token保存在数据库,也可以选择保存在redis或session。

   @Override
public SysUserToken createToken(long userId) {
// 生成一个token
String token = TokenGenerator.generateToken();
// 当前时间
Date now = new Date();
// 过期时间
Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
// 判断是否生成过token
SysUserToken sysUserToken = findByUserId(userId);
if(sysUserToken == null){
sysUserToken = new SysUserToken();
sysUserToken.setUserId(userId);
sysUserToken.setToken(token);
sysUserToken.setLastUpdateTime(now);
sysUserToken.setExpireTime(expireTime);
// 保存token,这里选择保存到数据库,也可以放到Redis或Session之类可存储的地方
save(sysUserToken);
} else{
sysUserToken.setToken(token);
sysUserToken.setLastUpdateTime(now);
sysUserToken.setExpireTime(expireTime);
// 如果token已经生成,则更新token的过期时间
update(sysUserToken);
}
return sysUserToken;
}

登录测试

登录 Swagger: localhost:8088/swagger-ui.html

用户名:admin 密码: admin

登录成功之后,会返回token,如下图所示。

登录成功之后,一般的逻辑是调到主页,这里我们可以继续访问一个接口当作登录成功之后的跳转(如 /dept/findTree,不用传参方便)。

然后我们就会发现调用失败,甚至打断点到目标接口代码,连接口代码都没有进来,根本没有调用到findTree接口。

这是必然的,因为引入乐Shiro之后便有了权限认证,如果访问请求没有携带token是不能通过验证的,具体解决方案参加下面的登录流程。

登录流程

为了帮助大家理解 shiro 的工作流程,这里对使用了 shiro 以后,我们项目的登录流程做一下简单的说明。

我们开启Debug模式,给登录接口及过滤器和认证器都打上断点,调用登录接口,跟着代码移动的脚步来了解整个登录的流程。

首先代码来到了我们调用的接口: login

成功验证用户密码,即将生成和保存token

根据条件生成或更新token,成功后登录接口会将token返回给前台,前台会带上token进入登录验证

登录接口返回之后就已经登录成功了,按照一般逻辑,这时就会跳转到主页了,我们这边没有页面,就通过访问接口来模拟吧。

我们访问Swagger里 dept/findTree 接口,获取机构数据,这个接口不用传参,比较方便。

结果发现访问没有访问正常结果,甚至debug发现连对应的后台接口代码都没有进去。那是因为加了shiro以后,访问除配置放过外的接口都是需要验证的。

我们直接在浏览器访问:http://localhost:8088/dept/findTree,发现代码来到了我们在过滤器设置的断点里边。

因为我们访问接口的时候,没有把刚才登录成功之后返回的token信息携带过来,所以在过滤器里验证token失败,返回"invalid token" 提示

果然,在代码执行完毕之后,页面得到 “invalid token” 的提示,那我们要继续访问还得带上token才行。

那怎样才能让 swagger 发送请求的时候把 token 也带过去呢,我们这样处理。

修改 Swagger 配置,添加请求头参数,用来传递 token。

SwaggerConfig.java

package com.louis.kitty.boot.config;
import java.util.ArrayList;
import java.util.List; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration
@EnableSwagger2
public class SwaggerConfig { @Bean
public Docket createRestApi(){
// 添加请求参数,我们这里把token作为请求头部参数传入后端
ParameterBuilder parameterBuilder = new ParameterBuilder();
List<Parameter> parameters = new ArrayList<Parameter>();
parameterBuilder.name("token").description("令牌")
.modelRef(new ModelRef("string")).parameterType("header").required(false).build();
parameters.add(parameterBuilder.build());
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.any()).paths(PathSelectors.any())
.build().globalOperationParameters(parameters);
// return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
// .select()
// .apis(RequestHandlerSelectors.any())
// .paths(PathSelectors.any()).build();
} private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("Kitty API Doc")
.description("This is a restful api document of Kitty.")
.version("1.0")
.build();
} }

重启代码,发现接口页面已经多了token请求参数了。

我们先调用登录接口,拿到返回的token之后,把token复制过来一起发送过去。

继续用 amdin 用户登录,获得返回 token

携带 token 再次访问 findTree 接口。

代码进入过滤器,发现 token 已经成功传过来了,往下执行 executeLogin 继续登录流程。

上面方法调用下面的接口,尝试从请求头或请求参数中获取token。

父类的 executeLogin 方法调用 createToken 创建 token,然后使用 Subject 进行登录。

过滤器的 createToken 方法返回我们自定义的 token 对象。

Subject 调用 SecurityManager 继续进行登录流程。

看下面的调用栈截图,经过系列操作之后,终于来到了我们的 OAuth2Realm,这里有我们的登录和授权逻辑。

来到 OAuth2Realm 的 doGetAuthenticationInfo 方法,将前台传递的token跟后台存储的做比对,比对成功继续往下走。

验证成功之后,代码终于来到了我们的目标接口,成功的完成了调用。

继续往前,放行代码,代码执行完毕,调用界面成功的返回了结果。

我们不传 token 或者传一个不存在的 token 试试。

发现代码在过滤器验证的时候没有通过,返回 “Token 失效” 提示。

接口响应结果,提示 “token失效,请重新登录”。

最后注意:加了Shiro之后每次调试接口都需要传递token,对我们开发来说也是麻烦,如有需要可以通过以下方法取消验证。

在 ShiroConfig 配置类中,把接口路径映射到 anon 过滤器,调试时就不需要 token 验证了。

Spring Boot + Spring Cloud 实现权限管理系统 (集成 Shiro 框架)的更多相关文章

  1. Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

    本文是接着上篇博客写的:Spring boot 入门(三):SpringBoot 集成结合 AdminLTE(Freemarker),利用 generate 自动生成代码,利用 DataTable 和 ...

  2. Spring Boot 入门(五):集成 AOP 进行日志管理

    本篇文章是接着 Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理写的,按照前面几篇博客的教程,可以搭建一个简单的项目,主要包含了 Pagehelper+MyBatis 分页 ...

  3. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十一):集成 Shiro 框架

    Apache Shiro 优势特点 它是一个功能强大.灵活的,优秀开源的安全框架. 它可以处理身份验证.授权.企业会话管理和加密. 它易于使用和理解,相比Spring Security入门门槛低. 主 ...

  4. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(七):集成 Druid 数据源

    数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏 ...

  5. 新书上线:《Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统》,欢迎大家买回去垫椅子垫桌脚

    新书上线 大家好,笔者的新书<Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统>已上线,此书内容充实.材质优良,乃家中必备垫桌脚 ...

  6. Spring Boot 2.X(十八):集成 Spring Security-登录认证和权限控制

    前言 在企业项目开发中,对系统的安全和权限控制往往是必需的,常见的安全框架有 Spring Security.Apache Shiro 等.本文主要简单介绍一下 Spring Security,再通过 ...

  7. [权限管理系统(四)]-spring boot +spring security短信认证+redis整合

    [权限管理系统]spring boot +spring security短信认证+redis整合   现在主流的登录方式主要有 3 种:账号密码登录.短信验证码登录和第三方授权登录,前面一节Sprin ...

  8. Spring Boot 入门(六):集成 treetable 和 zTree 实现树形图

    本篇文章是接着Spring Boot 入门(五):集成 AOP 进行日志管理写的,主要集成了树形图,在部门列表或者权限列表中,树形图经常被用上.主要是根据相应的 API 凭借 html 字符串 1.t ...

  9. Spring -> Spring Boot > Spring Cloud

    这几天刚刚上班,公司用的是Spring Cloud,接触不多.我得赶快学起来. 想学习就必须得知道什么是微服务,什么是Spring Boot,什么是Spring Cloud,以及两者之间有什么关系? ...

随机推荐

  1. Error updating database:线程异常

    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".SLF4J: See http://www.slf4 ...

  2. Jmeter 接口测试知识梳理——应用基础篇

    Jmeter 使用也有很长时间了,但是一直没有做一下知识梳理,近期会对公司同事做一下这方面的培训,借此机会,把使用过程中应用到的知识,或是遇到的问题,整理出来,方便大家学习! Jmeter 接口测试知 ...

  3. 配置java环境jdk

    最近尝试改公司的项目中的一个后台管理系统,前后台都让我一个做,所以要配置一下java环境: 1. 按装jdk 1.6//2. 安装eclipse3. 安装maven4. 安装eclispe的maven ...

  4. hibernate例子

    首先下载好hibernate包 如果运行之后出现数据库连接错误  则重新把jar包位置放在web-inf的lib下面再addbulidpath 第一步:创建实体类 Hibernate要求实体类必须提供 ...

  5. eclipse报错:Could not resolve bean definition resource pattern [classpath:spring/applicationContext-*.xml]或者找不到

    1.把xml文件复制到WEB-INF下 2.路径改成 [/WEB-INF/spring/applicationContext-*.xml]

  6. 《HTTP 权威指南》笔记:第十二章 基本认证体制

    导言 客户端可以通过网络来得到想要的信息,但是有一些信息并不能是对所有人都能看到的,因此必须有一种认证机制.服务器需要通过这种方式来了解用户身份,一旦服务器知道了用户的身份,就可以让用户能够访问请求的 ...

  7. top 内存mem的used很高,或者100%

    top 内存mem的used很高,或者100% Linux服务器运行一段时间后,由于其内存管理机制,会将暂时不用的内存转为buff/cache,这样在程序使用到这一部分数据时,能够很快的取出,从而提高 ...

  8. TF-IDF的解释

    转自:http://www.cnblogs.com/gongxijun/p/8673241.html TF(词频):  假定存在一份有N个词的文件A,其中‘明星‘这个词出现的次数为T.那么 TF = ...

  9. Spring Batch @EnableBatchIntegration 注解

    设置一个远程分块任务需要定义一系列的 beans: 一个连接工程来从消息中间件中获得连接,消息中间件包括有(JMS,AMQP 和其他) 一个 MessagingTemplate  来从主向从发送消息, ...

  10. Mysql 中如何创建数据库和数据表

    这里的数据库为:user  数据表为 aaa mysql –uroot –p                 进入mysql create database user;            创建数据 ...