一、引子

最近项目需要添加权限拦截,经讨论决定采用spring security4.2.2!废话少说直接上干货!

spring security 4.2.2文档:http://docs.spring.io/spring-security/site/docs/4.2.2.RELEASE/reference/htmlsingle/#el-access-web

spring security 3 中文2文档:http://www.mossle.com/docs/auth/html/index.html

二、pom.xml配置

需要在pom.xml里配置spring security的依赖,可以通过http://mvnrepository.com/search?q=spring+securitye查询不同版本需要的spring的版本支持。

   <!--spring security  -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.2.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.2.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>

三、web.xml配置

需要在web.xml中配置Spring Security控制权限过滤器,

   <filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

四、spring-security.xml配置

下面最主要的就是spring-security.xml的配置了

 <?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd" > <!-- 打印调试信息,仅在开发环境中使用 -->
<!-- <debug/> --> <!-- 不需要被拦截的请求 -->
<http pattern="/loginPage" security="none"/>
<http pattern="/scripts/**" security="none"/> <!--
登录页面可以使用第一种:
<http pattern="/login.jsp" security="none"></http>
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" ......
这种方式直接访问.jsp
也可以使用Controller来控制,两种方式登录页面login.jsp的位置不一样!!!
下面是第二种方式:
<http pattern="/login" security="none"></http>
<form-login login-page="/login" login-processing-url="/login" ......
第一个配置告诉spring security,类似于/login的url请求不做过滤处理,而第二个配置信息又告诉spring security url为/login的post请求登录请求处理。正是这种冲突导致了405错误的发生。
既然知道了错误原因,那么只要避免冲突就能解决这个问题:使登录页的请求和登录处理的请求不一致,然后只配置登录页的请求不做拦截处理.
我采用的方法是使用默认的登录URL /login,修改登录页面跳转url为/loginPage。请自行修改代码和配置信息
这样是不是就大工告成了呢?很遗憾,当你启动程序时,输出用户名和密码,不管正确与否,都会有404 Not Found等着你,这又是为什么呢?
登录请求404错误
首先,建议你看一下spring security自动生成的登录页源码,你会发现有如下代码
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
对于什么是csrf,请自行参考网上的资料。
spring security默认情况下csrf protection是开启的,由于我们的登录页没有配置csrf的相关信息,因此spring security内置的过滤器将此链接置为无效链接
解决办法就是配置csrf protection为不可用状态,在配置文件中增加
<csrf disabled="true"/>
--> <!--
1、<http auto-config="true">,他可以自动配置login form,BSIC 认证和logout URL 和logout services,如果没有特殊表明,这个的默认值是false。想要自己配置则设置为"true"
2、Spring Security采用的是一种就近原则,就是说当用户访问的url资源满足多个intercepter-url时,系统将使用第一个符合条件的intercept-url进行权限控制
-->
<http auto-config="true" use-expressions="true">
<!--
另一种权限表达式:
<http use-expressions="false">
<intercept-url pattern='/**' access='ROLE_USER' />
-->
<!-- 禁用CSRF保护,默认是启用 -->
<csrf disabled="true"/> <anonymous enabled="false"/> <!-- 基于角色认证(必须拥有ROLE_XXX角色才能访问所有/**/XXX/**资源) -->
<!-- 确保对功能URL访问都需要权限 -->
<intercept-url pattern="/**/add/**" access="hasRole('ADD')"/>
<intercept-url pattern="/**/update/**" access="hasRole('UPDATE')"/>
<intercept-url pattern="/**/delete/**" access="hasRole('DELETE')"/>
<intercept-url pattern="/**/download/**" access="hasRole('DOWNLOAD')"/>
<intercept-url pattern="/**/access/**" access="hasRole('ADMIN')"/> <!-- Ensures that any request to our application requires the user to be authenticated -->
<intercept-url pattern="/**" access="authenticated"/> <!--
实现免登陆验证,默认有效时间是两周,启用rememberMe之后的两周内,用户都可以直接跳过系统,直接进入系统。
实际上,Spring Security中的rememberMe是依赖cookie实现的,当用户在登录时选择使用rememberMe,系统就会在登录成功后将为用户生成一个唯一标识,并将这个标识保存进cookie中
Spring Security生成的cookie名称是SPRING_SECURITY_REMEMBER_ME_COOKIE,它的内容是一串加密的字符串,
当用户再次访问系统时,Spring Security将从这个cookie读取用户信息,并加以验证。如果可以证实cookie有效,就会自动将用户登录到系统中,并为用户授予对应的权限。
--> <!--
<remember-me remember-me-parameter="remember-me"
data-source-ref="dataSource"/>
spring security还提供了remember me的另一种相对更安全的实现机制 :在客户端的cookie中,仅保存一个无意义的加密串(与用户名、密码等敏感数据无关),然后在db中保存该加密串-用户信息的对应关系,自动登录时,用cookie中的加密串,到db中验证,如果通过,自动登录才算通过。会自动在你的数据库里创建一个表:PERSISTENT_LOGINS。
如果不加data-source-ref="dataSource",会将上述信息放在内存中!
作者的项目有些特殊要求,所有采用了下面的方式:登录后要在myAuthenticationSuccessHandler做特殊的处理!
-->
<remember-me authentication-success-handler-ref="myAuthenticationSuccessHandler"/> <!--
login-page : 表示用户登陆时显示我们自定义的登录页面
authentication-failure-url : 登录认证失败转向的url,当用户输入的登录名和密码不正确时,系统将再次跳转到登录页面,并添加一个error=true参数作为登陆失败的标示,这个标识是我们自定义的。
default-target-url : 登录认证成功转向的地址
-->
<form-login
login-page="/loginPage"
authentication-failure-url="/loginPage?error=true"
authentication-success-handler-ref="myAuthenticationSuccessHandler"
/> <!-- 登出后,返回到登陆页面 -->
<!-- <logout logout-success-url="/loginPage" logout-url="/logout"/>
delete-cookies="JSESSIONID":退出删除JSESSIONID
-->
<logout />
<!--
控制同步session的过滤器
如果concurrency-control标签配置了error-if-maximum-exceeded="true",max-sessions="1",那么第二次登录时,是登录不了的。
如果error-if-maximum-exceeded="false",那么第二次是能够登录到系统的,
但是第一个登录的账号再次发起请求时,会跳转到expired-url配置的url中,
如果没有配置expired-url,则显示:
This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).
翻译过来意思就是说:这个会话已经过期(可能由于多个并发登录尝试相同的用户)
--> <session-management invalid-session-url="/loginPage" >
<concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/loginPage"/>
</session-management> <!-- 指定自己的权限验证过滤器,首先走自己的的过滤器 myFilter,如果被拦截就报没有权限;
如果通过会走spring security自带的拦截器,即上面配置的权限配置!
-->
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myFilter"/>
</http> <!-- 权限认证Spring日志监听器 -->
<beans:bean class="org.springframework.security.authentication.event.LoggerListener"/>
<beans:bean class="org.springframework.security.access.event.LoggerListener"/> <!--
一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
我们的所有控制将在这三个类中实现,解释详见具体配置。
-->
<beans:bean id="myFilter" class="com.tcbd.common.interceptor.MyFilterSecurityInterceptor" >
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean> <!-- 验证配置,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-manager alias="authenticationManager" >
<authentication-provider ref="daoAuthenticationProvider" >
</authentication-provider>
</authentication-manager> <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="myUserDetailService" />
<beans:property name="passwordEncoder" ref="passwordEncoder" />
</beans:bean>
<!-- spring推荐的单向加密算法 -->
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> <!-- 在这个类中,读入用户的密码,角色信息,是否锁定,账号是否过期等属性信息 -->
<beans:bean id="myUserDetailService" class="com.tcbd.common.interceptor.MyUserDetailService" /> <!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
<beans:bean id="myAccessDecisionManagerBean" class="com.tcbd.common.interceptor.MyAccessDecisionManager" ></beans:bean> <!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->
<beans:bean id="securityMetadataSource" class="com.tcbd.common.interceptor.MyFilterSecurityMetadataSource" /> <beans:bean id="myAuthenticationSuccessHandler" class="com.tcbd.common.interceptor.MyAuthenticationSuccessHandler"/> </beans:beans>

spring security为我们提供了三种通配符。
通配符:?
示例:/admin/g?t.jsp
匹配任意一个字符,/admin/g?t.jsp可以匹配/admin/get.jsp和/admin/got.jsp或是/admin/gxt.do。不能匹配/admin/xxx.jsp。
通配符:*
示例:/admin/*.jsp
匹配任意多个字符,但不能跨越目录。/*/index.jsp可以匹配/admin/index.jsp和/user/index.jsp,但是不能匹配/index.jsp和/user/test/index.jsp。
通配符:**
示例:/**/index.jsp
可以匹配任意多个字符,可以跨越目录,可以匹配/index.jsp,/admin/index.jsp,/user/admin/index.jsp和/a/b/c/d/index.jsp

五、自己的拦截器

1,读入用户的密码,角色信息,是否锁定,账号是否过期等属性信息 :MyUserDetailService

 import java.util.ArrayList;
import java.util.Collection; import javax.annotation.Resource; import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; /**
* 从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等
*
*/
public class MyUserDetailService implements UserDetailsService {
@Resource
private UserService userService; /**
* 数据库交互获取用户拥有的权限角色,并设置权限
*/
@Override
public UserDetails loadUserByUsername(String userCode) throws UsernameNotFoundException, DataAccessException {
// 根据登录用户名获取用户信息
Users user = new Users();
user.setUserCode(username);
user = userService.selectByModel(user);
if (null != user) {
// 存放权限
Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
String action = user.getRole();
if (StringUtils.isNotBlank(action)) {
String[] roleaCtion = action.split(",");
for (int i = 0; i < roleaCtion.length; i++) {
SimpleGrantedAuthority auth = new SimpleGrantedAuthority(roleaCtion[i]);
auths.add(auth);
}
}
//spring security自带的User对象
User userDetails = new User(username, user.getPassword(), true, true, true, true, auths);
return userDetails;
}
return null;
}
}

2,定义某一资源可以被哪些角色访问:MyFilterSecurityMetadataSource

 import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static Logger logger = Logger.getLogger(MyFilterSecurityMetadataSource.class); public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
HttpServletRequest request = fi.getRequest();
String requestUrl = fi.getRequest().getRequestURI();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
// 所有URL对应的角色,应用启动就存放到静态资源里,得到的结果是:不同的URL下,包含的多个角色
Map<String, String> resRoles = Constant.URL_ROLES; for (Map.Entry<String, String> ent : resRoles.entrySet()) {
String url = ent.getKey();
String roles = ent.getValue();
//根据业务写自己的匹配逻辑
if(requestUrl.startsWith(url)){
attributes.addAll(SecurityConfig.createListFromCommaDelimitedString(roles));
}
}
logger.debug("【"+request.getRequestURI()+"】 roles: "+attributes);
return attributes;
} public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
} public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}

3,访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源:MyAccessDecisionManager

 import java.util.Collection;
import java.util.Iterator; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; /**
* 在这种方法中,需要与configAttributes比较验证
* 1、一个对象是一个URL,一个过滤器被这个URL找到权限配置,并通过这里
* 2、如果没有匹配相应的认证,AccessDeniedException
*
*/
public class MyAccessDecisionManager implements AccessDecisionManager { private static final Log logger = LogFactory.getLog(MyAccessDecisionManager.class); /**
* 在这个类中,最重要的是decide方法,如果不存在对该资源的定义,直接放行; 否则,如果找到正确的角色,即认为拥有权限,并放行,否则throw
* new AccessDeniedException("no right");这样,就会进入上面提到的/accessDenied.jsp页面。
* @param authentication :当前用户所且有的角色
* @param object :当前请求的URL
* @param configAttributes :当前URL所且有的角色
*/
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
// 资源所需的角色列表,如果角色列表为空,则放行!继续下一个拦截器。
if (configAttributes == null) {
return;
}
// 即将访问的资源URL,如 : /admin.jsp
logger.info("URL :"+object);
// 遍历所需的角色集合
Iterator<ConfigAttribute> ite = configAttributes.iterator();
while (ite.hasNext()) {
ConfigAttribute ca = ite.next();
// 该资源所需要的角色
String needRole = ((SecurityConfig) ca).getAttribute();
// authentication.getAuthorities()获取用户所拥有的角色列表,如:OLE_DEFULT
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
// 将资源所需要的角色与用户拥有的角色比较
if (needRole.equals(grantedAuthority.getAuthority())) {
// grantedAuthority is user's role.
// 角色相同,直接放行
return;
}
}
}
// 否则,提示没有权限访问该资源
throw new AccessDeniedException("no right");
} @Override
public boolean supports(ConfigAttribute attribute) {
return true;
} @Override
public boolean supports(Class<?> clazz) {
return true;
} }

4,拦截器核心,MyFilterSecurityInterceptor

 import java.io.IOException;

 import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; /**
*
*/
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor
implements Filter { private FilterInvocationSecurityMetadataSource securityMetadataSource; /* get、set方法 */
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
} public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
} @Override
public Class<? extends Object> getSecureObjectClass() {
return FilterInvocation.class;
} @Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
} @Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
} public void invoke(FilterInvocation fi) throws IOException,
ServletException {
/**
* 最核心的代码就是@link InterceptorStatusToken token = super.beforeInvocation(fi);
* 它会调用我们定义的MyInvocationSecurityMetadataSource.getAttributes方法和MyAccessDecisionManager.decide方法
* 这一句,即在执行doFilter之前,进行权限的检查,而具体的实现已经交给@link MyAccessDecisionManager 了
*/
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//继续走下一个拦截器,也就是org.springframework.security.web.access.intercept.FilterSecurityInterceptor
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
} @Override
public void destroy() { } @Override
public void init(FilterConfig arg0) throws ServletException { } }

5,登录页面(需要自己改一些东西)

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix='fmt' uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/favicon.ico"> <title><fmt:message key="application.title"></fmt:message></title> <!-- Bootstrap core CSS -->
<link href="/scripts/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- Custom styles for this template --> <link href="/scripts/apps/css/signin.css" rel="stylesheet"> <style type="text/css">
.tip {
font-size: 10px;
color: red;
text-align: center;
} </style>
</head> <body>
<div class="container">
<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" id="login_form" class="form-signin" method="post">
<div class="form-signin-heading text-center">
<h2 ><fmt:message key="application.title"></fmt:message></h2>
</div>
<div id="tip" class="tip"></div>
<c:if test="${param.error != null}">
<span style="color:red">用户名或密码有误</span>
</c:if> <label for="inputEmail" class="sr-only">登录帐号</label>
<input type="text" id="inputUserCode" name="username" class="form-control" placeholder="登录帐号" autofocus>
<label for="inputPassword" class="sr-only">登录密码</label>
<input type="password" id="inputPassword" name="password" class="form-control" placeholder="登录密码">
<div class="input-group input-sm">
<div class="checkbox">
<label><input type="checkbox" id="rememberme" name="remember-me" value="true"> 下次自动登录</label>
</div>
</div>
<button id="login_button" class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
</form> </div> <script src="/scripts/plugins/jQuery/jquery-2.2.3.min.js"></script>
</body>
</html>

在JSP中相应的操作按钮上加上如下标签,控制显示:

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> 该标签一定要加上!!!
<sec:authorize access="hasRole('ADD')">
<a href="/XXX/add">增加</a>
</sec:authorize>

数据库设计:user表里有roleId,role表,resource-role表,resource表

spring security4.2.2的maven配置+spring-security配置详解+java源码+数据库设计的更多相关文章

  1. Spring Boot启动命令参数详解及源码分析

    使用过Spring Boot,我们都知道通过java -jar可以快速启动Spring Boot项目.同时,也可以通过在执行jar -jar时传递参数来进行配置.本文带大家系统的了解一下Spring ...

  2. Spring IOC容器启动流程源码解析(一)——容器概念详解及源码初探

    目录 1. 前言 1.1 IOC容器到底是什么 1.2 BeanFactory和ApplicationContext的联系以及区别 1.3 解读IOC容器启动流程的意义 1.4 如何有效的阅读源码 2 ...

  3. Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]

    目录 前言 现象 源码分析 实战例子 总结 参考资料 前言 今天研究了一下tomcat上web.xml配置文件中url-pattern的问题. 这个问题其实毕业前就困扰着我,当时忙于找工作. 找到工作 ...

  4. Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍

    Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍 spring集成 mybatis Spring4.x零配置框架搭建 两年前一直在做后 ...

  5. Spring Security Filter详解

    Spring Security Filter详解 汇总 Filter 作用 DelegatingFilterProxy Spring Security基于这个Filter建立拦截机制 Abstract ...

  6. 整合Spring时Service层为什么不做全局包扫描详解

    合Spring时Service层为什么不做全局包扫描详解 一.Spring和SpringMVC的父子容器关系 1.讲问题之前要先明白一个关系 一般来说,我们在整合Spring和SpringMVC这两个 ...

  7. spring security之 默认登录页源码跟踪

    spring security之 默认登录页源码跟踪 ​ 2021年的最后2个月,立个flag,要把Spring Security和Spring Security OAuth2的应用及主流程源码研究透 ...

  8. spring security 授权方式(自定义)及源码跟踪

    spring security 授权方式(自定义)及源码跟踪 ​ 这节我们来看看spring security的几种授权方式,及简要的源码跟踪.在初步接触spring security时,为了实现它的 ...

  9. spring盒springMVC整合父子容器问题:整合Spring时Service层为什么不做全局包扫描详解

    整合Spring时Service层为什么不做全局包扫描详解 一.Spring和SpringMVC的父子容器关系 1.讲问题之前要先明白一个关系 一般来说,我们在整合Spring和SpringMVC这两 ...

随机推荐

  1. Mybatis中文查询没有结果

    我用中文参数去查找数据,没有返回结果,应该是乱码问题 进行如下配置问题消失:jdbc:mysql://localhost:3306/appstore_db?useUnicode=true&ch ...

  2. Order Management Suite - Pricing and Availability Form Library

    In this Document   Purpose   Scope   Details   A. Form / Functional Issues   "Add to Selection& ...

  3. Sencha touch API

    Sencha touch  API http://docs.sencha.com/touch/2.3.1/#!/guide/getting_started

  4. 如何将windows格式的图标作为os x应用程序的图标

    刚由windows转为os x的同学可能知道,在os x下我们想改变一个app的图标异常简单,直接打开该app的简介,然后将图标文件拖入简介窗口左上角图标方框即可.但是很多童鞋下载了一些图标文件后发现 ...

  5. jQuery如何停止元素的animate动画,还有怎样判断是否处于动画状态

    jquery的animation会自动进入队列,就出现了一个问题,这些动画会一一执行完成,而我们实际的本意是当鼠标移开的时候动画即终止. 停止元素的动画方法:stop()语法结构:stop([clea ...

  6. strtok函数读写冲突问题

    先上测试代码 #include "stdafx.h" #include <iostream> using namespace std; int _tmain(int a ...

  7. SSH框架组建时碰到的一些问题

    以前用spring+hibernate的框架解决后台事务,这一次重新组建框架,计划引入Struts,如果方便的话,可能会进一步引入Freemarker.以下记下配置中的一些问题及解决,以供他人参考. ...

  8. 【js-xlsx和file-saver插件】前端html的table导出数据到excel的表格合并显示boder

    最近在做项目,需要从页面的表格中导出excel,一般导出excel有两种方法:一.习惯上是建模版从后台服务程序中导出:二.根据页面table中导出:综合考虑其中利弊选择二.根据页面table中导出ex ...

  9. js如何读写txt文件?(曲线救国篇)

    .emmm,不存在的.做不到的. 但是,你可以继续往下阅读,或许能实现你想要的功能. 前言:一般我们需要用js来实现文件读写,都是一些比较小的,离线的应用(因为如果可以联网,什么json什么db都任意 ...

  10. Ocelot中文文档-GraphQL

    好吧!你明白我的意思Ocelot并不直接支持GraphQL,但有这么多人问起它,我想表明整合graphql-dotnet库是多么容易 请参阅示例项目OcelotGraphQL. 结合使用graphql ...