首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Spring Security3实践小结

2013-08-01 
Spring Security3实践总结注意:第5步的白名单是自定义操作。?三、实现????? 使用Spring Security3的实践过程

Spring Security3实践总结

注意:第5步的白名单是自定义操作。

?

三、实现

????? 使用Spring Security3的实践过程如下:

?

??1. 增加Spring Security3的MAVEN坐标依赖:

<!-- Spring Security Start --><dependency>  <groupId>org.springframework.security</groupId>  <artifactId>spring-security-web</artifactId>  <version>3.1.2.RELEASE</version></dependency><dependency>  <groupId>org.springframework.security</groupId>  <artifactId>spring-security-taglibs</artifactId>  <version>3.1.2.RELEASE</version></dependency><dependency>  <groupId>org.springframework.security</groupId>  <artifactId>spring-security-core</artifactId>  <version>3.1.2.RELEASE</version></dependency><dependency>  <groupId>org.springframework.security</groupId>  <artifactId>spring-security-config</artifactId>  <version>3.1.2.RELEASE</version></dependency><!-- Spring Security End -->

?

?2. 设置项目的web.xml,Spring Security3的过滤器:

<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>

??

3. 增加applicationContext-security.xml,整体配置在附件代码中。

?

? ? 3.1) 验证管理器配置,用户登陆的时候进行的验证,这里采用JDBC的模式,如果不用开启hideUserNotFoundExceptions,则可以采用简单的标签配置。

<!-- 认证管理器.用户登陆认证的入口, 加载用户角色。 --><authentication-manager alias="authenticationManagerBean"> <authentication-provider ref="authenticationProvider">  <!-- <jdbc-user-service data-source-ref="dataSource" users-by-username-query="" authorities-by-username-query="" />  <password-encoder base64="true" hash="sha-256" /> --> </authentication-provider></authentication-manager><b:bean id="authenticationProvider"  ref="userDetailsService" /> <b:property name="hideUserNotFoundExceptions" value="false" /> <b:property name="passwordEncoder" ref="passwordEncoder" /></b:bean><b:bean id="userDetailsService" ref="dataSource" /> <b:property name="usersByUsernameQuery"    value="SELECT username, password, enable FROM user WHERE username=? AND enable=true;" /> <b:property name="authoritiesByUsernameQuery"    value="SELECT u.username username, r.rolename rolename FROM user u, role r,    user_role ur WHERE u.username=? AND u.id = ur.user_id AND ur.role_id = r.id;" /></b:bean><!-- 用户的密码加密方式 --><b:bean id="passwordEncoder"  value="256" /> <b:property name="encodeHashAsBase64" value="true" /></b:bean>

?

? ? 3.2) 授权流程中安全资源加载器和决策器的配置:

<!-- 访问决策器,决定某个用户(具有的角色)是否有足够的权限去访问某个资源 --><b:bean id="accessDecisionManagerBean" > <!-- 没有显式定义的资源都保护起来。该属性默认值为false --> <b:property name="allowIfAllAbstainDecisions" value="false"/></b:bean><!-- 安全资源定义,即定义某一安全资源可以被哪些角色访问 --><b:bean id="securityMetadataSourceBean" ref="commonDao" /> <b:property name="rejectPublicInvocations" value="true" /></b:bean>

?

? ? 3.3) 指定过滤器,作为验证和授权的自定义处理入口

<!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性 --> <b:bean id="customizedFilter" ref="authenticationManagerBean" /> <b:property name="accessDecisionManager" ref="accessDecisionManagerBean" /> <b:property name="securityMetadataSource" ref="securityMetadataSourceBean" /></b:bean>

?

? ? 3.4) 同时增加异常处理配置,针对验证过程中的异常记性处理

<!-- 认证异常处理 --><b:bean id="exceptionMappingAuthenticationFailureHandler"  value="/login.jsp?sign=No User" />   <!-- 凭证错误(密码不正确) -->   <b:entry key="org.springframework.security.authentication.BadCredentialsException"  value="/login.jsp?sign=Bad Credentials" />   <!-- 用户不可用 -->   <b:entry key="org.springframework.security.authentication.DisabledException"  value="/login.jsp?sign=User is disabled" />   <!-- 登陆凭证错误 -->   <b:entry key="org.springframework.security.core.AuthenticationException"  value="/login.jsp?sign=Authentication Failure" />  </b:map> </b:property></b:bean>

?

? ? 3.5) 现在将上述的整体将上述配置整体配置到SpringSecurity3的过滤器链中:

<!-- 当访问被拒绝时,会转到403.jsp --><http access-denied-page="/WEB-INF/error/403.jsp" pattern="/*.htm*">  <!-- 登陆设置 -->  <form-login login-page="/login.jsp" username-parameter="loginName"     password-parameter="password" login-processing-url="/login.htm"    authentication-failure-url="/login.jsp?sign=BadCredentials" default-target-url="/index.htm" always-use-default-target="true"     authentication-failure-handler-ref="exceptionMappingAuthenticationFailureHandler" />  <!-- 匿名用户访问控制,这里设置不允许匿名用户登陆 -->  <anonymous enabled="false" />  <!-- 登出设置 -->  <logout logout-success-url="/login.jsp" logout-url="/logout.htm" />  <http-basic />  <!-- 增加一个filter,位于FILTER_SECURITY_INTERCEPTOR之前 -->  <custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="customizedFilter" /></http>

?

? ? 3.6) 同时增加自身项目需要白名单设置:

<!-- 安全资源白名单(URL) --><b:bean id="securityMetadataSourceTrustListHolder" ><b:property name="trustList"><b:list><b:value>/index.htm</b:value><b:value>/hello.htm</b:value></b:list></b:property></b:bean><!-- 安全用户白名单 --><b:bean id="securityUserTrustListHolder" ><b:property name="trustList"><b:list><b:value>administrator</b:value></b:list></b:property></b:bean>

?

? ? 3.7) 开启SpringSecurity3日志:

<!-- 开启Spring Security3认证和授权日志 --><b:bean name="code">/** * 权限拦截器 *  * @author Watson Xu * @since 1.0.7 <p>2013-7-10 下午4:12:18</p> */public class CustomizedFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {private FilterInvocationSecurityMetadataSource securityMetadataSource;private static Log logger = LogFactory.getLog(CustomizedFilterSecurityInterceptor.class);// ~ Methods// ========================================================================================================/** * Method that is actually called by the filter chain. Simply delegates to * the {@link #invoke(FilterInvocation)} method. *  * @param request  the servlet request * @param response   the servlet response * @param chain  the filter chain * @throws IOException   if the filter chain fails * @throws ServletException if the filter chain fails */public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {//@1HttpServletRequest httpRequest = (HttpServletRequest)request;HttpServletResponse httpResponse = (HttpServletResponse)response;String url = httpRequest.getRequestURI().replaceFirst(httpRequest.getContextPath(), "");//1.1)判断登陆状态:如果未登陆则要求登陆if(!SessionUserDetailsUtil.isLogined()) {httpResponse.sendRedirect(httpRequest.getContextPath() + SecurityConstants.LOGIN_URL);logger.info("未登陆用户,From IP:" + SecutiryRequestUtil.getRequestIp(httpRequest) + "访问 :URI" + url);return;    }//1.2)过用户白名单:如果为超级管理员,则直接执行if(SecurityUserTrustListHolder.isTrustUser(SessionUserDetailsUtil.getLoginUserName())) {chain.doFilter(request, response);return;}//1.3)过资源(URL)白名单:如果为公共页面,直接执行if(SecurityMetadataSourceTrustListHolder.isTrustSecurityMetadataSource(url)){chain.doFilter(request, response);return;}FilterInvocation fi = new FilterInvocation(request, response, chain);invoke(fi);}public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {return this.securityMetadataSource;}public Class<? extends Object> getSecureObjectClass() {return FilterInvocation.class;}public void invoke(FilterInvocation fi) throws IOException,ServletException {//@2,进行安全验证InterceptorStatusToken token = null;try {token = super.beforeInvocation(fi);} catch (IllegalArgumentException e) {HttpServletRequest httpRequest = fi.getRequest();HttpServletResponse httpResponse = fi.getResponse();String url = httpRequest.getRequestURI().replaceFirst(httpRequest.getContextPath(), "");logger.info("用户 " + SessionUserDetailsUtil.getLoginUserName() + ",From IP:" + SecutiryRequestUtil.getRequestIp(httpRequest) + "。尝试访问未授权(或者) URI:" + url);httpResponse.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);            RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(SecurityConstants.NOT_ACCEPTABLE);            dispatcher.forward(httpRequest, httpResponse);return;}try {fi.getChain().doFilter(fi.getRequest(), fi.getResponse());} finally {super.afterInvocation(token, null);}}public SecurityMetadataSource obtainSecurityMetadataSource() {return this.securityMetadataSource;}public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {this.securityMetadataSource = newSource;}@Overridepublic void destroy() {}@Overridepublic void init(FilterConfig arg0) throws ServletException {}}

?

??5. 自定义资源的访问权限的定义加载器

/** * 安全资源(URL)和角色映射关系处理器 *  * @author Watson Xu * @since 1.0.7 <p>2013-7-9 下午3:25:09</p> */public class CustomizedInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {private boolean rejectPublicInvocations = false;private CommonDao dao;private static Map<String, Integer> resources = new HashMap<String, Integer>();public CustomizedInvocationSecurityMetadataSource(CommonDao dao) {this.dao = dao;loadSecurityMetadataSource();}// According to a URL, Find out permission configuration of this URL.// 根据URL来查找所有能够访问该资源的角色。public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {// guess object is a URL.//@3String url = ((FilterInvocation)object).getRequestUrl();if(resources.isEmpty()) return null;Integer resourceId = resources.get(url);if(rejectPublicInvocations && resourceId == null) {throw new IllegalArgumentException("Secure object invocation " + object +                    " was denied as public invocations are not allowed via this interceptor. ");//请求不存在}return getRolesByResouceId(resourceId);}private Collection<ConfigAttribute> getRolesByResouceId(Integer resourceId) {List<String>  roles = dao.getRoleByResourceId(resourceId);Collection<ConfigAttribute> atts = null;if(roles != null) {atts = new ArrayList<ConfigAttribute>();for (String role : roles) {atts.add(new SecurityConfig(role));}}return atts;}//加载所有安全资源(URL)private void loadSecurityMetadataSource() {List<Map<String, Object>> resourceDtos = dao.getAllResource();if(resourceDtos != null) {resources.clear();for (Map<String, Object> dto : resourceDtos) {resources.put(dto.get("url").toString(), Integer.parseInt(dto.get("id").toString())); }}}public boolean supports(Class<?> clazz) {return true;}public Collection<ConfigAttribute> getAllConfigAttributes() {return null;}public void setDao(CommonDao dao) {this.dao = dao;}public void setRejectPublicInvocations(boolean rejectPublicInvocations) {this.rejectPublicInvocations = rejectPublicInvocations;}}
/** * 安全资源(URL)权限决策器 * * @author Watson Xu * @since 1.0.7 <p>2013-7-10 下午4:08:42</p> */public class CustomizedAccessDecisionManager implements AccessDecisionManager {private boolean allowIfAllAbstainDecisions = false; public boolean isAllowIfAllAbstainDecisions() { return allowIfAllAbstainDecisions; } public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) { this.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions; } protected final void checkAllowIfAllAbstainDecisions() { if (!this.isAllowIfAllAbstainDecisions()) { throw new AccessDeniedException("Access is denied"); } } //In this method, need to compare authentication with configAttributes.// 1, A object is a URL, a filter was find permission configuration by this URL, and pass to here.// 2, Check authentication has attribute in permission configuration (configAttributes)// 3, If not match corresponding authentication, throw a AccessDeniedException.//这里的三个参数可以片面的理解为: 用户登录后的凭证(其中包含了用户名和角色的对应关系)、请求的URL、请求的URL对应角色(这些角色可以访问这些URL)public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)throws AccessDeniedException, InsufficientAuthenticationException {//@4if(configAttributes == null){return;}Iterator<ConfigAttribute> ite=configAttributes.iterator();while(ite.hasNext()){ConfigAttribute ca=ite.next();String needRole=((SecurityConfig)ca).getAttribute();for(GrantedAuthority ga:authentication.getAuthorities()){if(needRole.equals(ga.getAuthority())){ //ga is user's role.return;}}}checkAllowIfAllAbstainDecisions();}@Overridepublic boolean supports(ConfigAttribute attribute) {return true;}@Overridepublic boolean supports(Class<?> clazz) {return true;}}

?

? 6. 其他工具类代码这不做赘述,具体可以参考附件中的工程代码。

?

参考:

    Spring Security官方文档:http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity.html官方文档中文翻译:http://www.mossle.com/docs/springsecurity3/html/springsecurity.html入门教程:http://www.blogjava.net/fastzch/archive/2013/05/02/315028.html更多的自定义选择:http://blog.csdn.net/shadowsick/article/details/8576449

?

热点排行