Maven+Spring security4.2.3的教程和源码

来源:互联网 发布:数据挖掘的发展前景 编辑:程序博客网 时间:2024/05/21 22:44

Maven+Spring security4.2.3的教程和源码

根据网上的教程,我们知道Spring security有四种方式,从简到深为:1、不用数据库,全部数据写在配置文件,这个也是官方文档里面的demo;2、使用数据库,根据spring security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不灵活,而且那个数据库设计得很简陋,实用性差;3、spring security和Acegi不同,它不能修改默认filter了,但支持插入filter,所以根据这个,我们可以插入自己的filter来灵活使用;4、暴力手段,修改源码,前面说的修改默认filter只是修改配置文件以替换filter而已,这种是直接改了里面的源码,但是这种不符合OO设计原则,而且不实际,不可用。

本文是根据第三中方式来实现安全控制的,大概流程如下网上是这样的我也觉得是这样的

现在先大概过一遍整个流程,用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。访问资源(即授权管理),访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面看完可能会蒙不打紧,源码里有些注释。

我觉的最最重要的spring—security.xml的配置,弄明白一些标签对于理解Spring security也是挺重要的,先看下目录结构


 

关闭的那些包都没用到可以不用管。

pom.xml中加入

 <!--spring security-->

    <dependency>

      <groupId>org.springframework.security</groupId>

      <artifactId>spring-security-web</artifactId>

      <version>4.2.3.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework.security</groupId>

      <artifactId>spring-security-config</artifactId>

      <version>4.2.3.RELEASE</version>

</dependency>

其他该干嘛、就干嘛,另外也贴出spring—security.xml,和web.Xml

Web.xml如下:

<?xml version="1.0" encoding="UTF-8" ?>

<web-app>

  <context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>classpath:spring-security.xml</param-value>

  </context-param>

  <!--配置spring listener-->

  <listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

  </listener>

  <!-- 防止Spring内存溢出监听器 -->

  <listener>

    <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>

  </listener>

  <!-- <!–解决POST乱码问题–>-->

  <filter>

    <filter-name>CharacterEncodingFilter</filter-name>

    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

    <init-param>

      <param-name>encoding</param-name>

      <param-value>utf-8</param-value>

    </init-param>

  </filter>

  <filter-mapping>

    <filter-name>CharacterEncodingFilter</filter-name>

    <url-pattern>/*</url-pattern>

  </filter-mapping>

  <!-- <!–springmvc前端控制器配置–>-->

  <servlet>

    <servlet-name>dispatcherServlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <init-param>

      <param-name>contextConfigLocation</param-name>

      <param-value>classpath:spring-application.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

  </servlet>

  <servlet-mapping>

    <servlet-name>dispatcherServlet</servlet-name>

    <url-pattern>/</url-pattern>

  </servlet-mapping>

 

  <!-- 权限  Spring Security3.1 的权限过滤-->

  <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的过滤器 –>-->

  <servlet-mapping>

    <servlet-name>default</servlet-name>

    <url-pattern>*.xml</url-pattern>

  </servlet-mapping>

  <servlet-mapping>

    <servlet-name>default</servlet-name>

    <url-pattern>*.css</url-pattern>

  </servlet-mapping>

</web-app>

下面就是spring—security.xml

 

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

             xmlns:beans="http://www.springframework.org/schema/beans"

             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

             xmlns:context="http://www.springframework.org/schema/context"

             xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

 

    <context:component-scan base-package="com.AdesK.*" />

    <!-- 开发环境可以放置 <debug /> 配置,会输出详细Security处理日志,正式环境建议屏蔽 <debug /> 配置

    <debug />-->

<!--设置那些资源可以访问,那些需要权限才能访问-->

    <http auto-config="true" use-expressions="true" >

        <intercept-url pattern="/login" access="permitAll" />

        <intercept-url pattern="/access_Denied" access="permitAll" />

        <intercept-url pattern="/WEB-INF/**" access="ROLE_USER" />

        <!--把相应的目录的资源设置权限-->

        <intercept-url pattern="/WEB-INF/views/**" access="isAuthenticated()" />

        <intercept-url pattern="/**" access="isAuthenticated()" />

        <!--起始页,前提必须要permitall-->

        <form-login

                login-page="/login"

                username-parameter="username"

                password-parameter="password"

                login-processing-url="/index"

                authentication-failure-url="/access_Denied"

                 authentication-success-forward-url="/index"/>

              <!--是否允许多用户用一个账号登陆-->

        <session-management >

            <concurrency-control max-sessions="1"

                                 error-if-maximum-exceeded="true" />

        </session-management>

        <custom-filter ref="mySecurityFilter" before="FILTER_SECURITY_INTERCEPTOR"></custom-filter>

        <csrf disabled="true"/>

    </http>

 

    <authentication-manager alias="MyAuthenticationManager">

        <authentication-provider user-service-ref="myUserDetailService">

            <!--如果用户的密码采用加密的话 <password-encoder hash="md5" /> -->

        </authentication-provider>

    </authentication-manager>

<!--可以用注解开发-->

    <beans:bean id="mySecurityFilter" class="com.AdesK.security.MySecurityFilter">

        <beans:property name="accessDecisionManager" ref="myAccessDecisionManager"/>

        <beans:property name="authenticationManager" ref="MyAuthenticationManager"/>

 

    </beans:bean>

 

    <beans:bean id="myUserDetailService" class="com.AdesK.security.MyUserDetailService"/>

 

    <beans:bean id="mySecurityMetadataSource" class="com.AdesK.security.MySecurityMetadataSource"/>

  <!--  <beans:bean id="myAuthenticationFilter" class="com.AdesK.security.MyAuthenticationFilter"/>-->

    <beans:bean id="myAccessDecisionManager" class="com.AdesK.security.MyAccessDecisionManager"/>

 

</beans:beans>

自定义的拦截器,要extends AbstractSecurityInterceptor implements Filter满足这种要求

MySecurityFilter.java

package com.AdesK.security;

 

import org.springframework.beans.factory.annotation.Autowired;

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 javax.annotation.PostConstruct;

import javax.servlet.*;

import java.io.IOException;

 

/**

 * @author AdesKng

 * @version 1.0

 * @TIME 2017/10/07-21:10

 * @E-mail 109

 */

public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter {

    //spring-security.xml里的myFilter的属性securityMetadataSource对应,

    //其他的两个组件,已经在AbstractSecurityInterceptor定义

    @Autowired

    private MySecurityMetadataSource mySecurityMetadataSource;

 

 

    @PostConstruct

    public void init(){

System.out.println(" ---------------  MySecurityFilter init--------------- ");

        /*super.setAuthenticationManager(myAuthenticationManager);

        super.setAccessDecisionManager(myAccessDecisionManager);*/

    }

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {

 

    }

 

    @Override

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println(" ---------------  doFilter--------------- ");

        FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);

        invoke(fi);

    }

 

    public void invoke(FilterInvocation fi) throws IOException, ServletException {

        //fi里面有一个被拦截的url

        //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限

        //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够

        InterceptorStatusToken token = super.beforeInvocation(fi);

        try {

            //执行下一个拦截器

            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());

        } finally {

            super.afterInvocation(token, null);

        }

    }

 

    @Override

    public void destroy() {

 

    }

 

    @Override

    public Class<?> getSecureObjectClass() {

        return FilterInvocation.class;

    }

 

    @Override

    public SecurityMetadataSource obtainSecurityMetadataSource() {

        return this.mySecurityMetadataSource;

    }

}

首先,登陆后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法,这个方法调用了invoke方法,其中fi断点显示是一个url,最重要的是beforeInvocation这个方法,它首先会调用MyInvocationSecurityMetadataSource类的getAttributes方法获取被拦截url所需的权限,在调用MyAccessDecisionManager类decide方法判断用户是否够权限。弄完这一切就会执行下一个拦截器。

下面MyAccessDecisionManager.java

package com.AdesK.security;

 

 

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;

 

 

import javax.annotation.PostConstruct;

import java.util.*;

 

/**

 * @author AdesKng

 * @version 1.0

 * @TIME 2017/10/07-21:19

 * @E-mail 109

 */

/*<!--资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->*/

 

public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

    @PostConstruct

    private void loadResourceDefine() {

        System.out.println("--------------- MySecurityMetadataSource   init--------------- ");

        resourceMap = new HashMap<String, Collection<ConfigAttribute>>();

        Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();

        //应该从数据库读取权限

        ConfigAttribute ca = new SecurityConfig("ROLE_USER");

        atts.add(ca);

        resourceMap.put("/index.jsp", atts);

 

        Collection<ConfigAttribute> attsno =new ArrayList<ConfigAttribute>();

        ConfigAttribute cano = new SecurityConfig("ROLE_NO");

        attsno.add(cano);

        resourceMap.put("/other.jsp", attsno);

    }

    //参数是要访问的url,返回这个url对于的所有权限(或角色)

    @Override

    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {

        String requestUrl =((FilterInvocation)o).getRequestUrl();

        if(resourceMap == null) {

            loadResourceDefine();

        }

        if(requestUrl.indexOf("?")>-1){

            requestUrl=requestUrl.substring(0,requestUrl.indexOf("?"));

        }

        System.out.println("--------------- "+requestUrl+"   --------------- ");

        return resourceMap.get(requestUrl);

    }

 

    @Override

    public Collection<ConfigAttribute> getAllConfigAttributes() {

        return null;

    }

 

    @Override

    public boolean supports(Class<?> aClass) {

        return true;

    }

}

还有几个类就不贴了很长,自己到后面的网址download,

这些代码也不完全是自己写的,博取了众家之长,加上自己的理解平凑出来的,可能你网上看到的第4版的写法和我的不同,那就对了,我是按照第3个版本写出来了,改了很多。源码地址附上

https://github.com/AdesKing/SSM-spring-security4.2.3

慢慢理解,所有东西都在项目的security包中,用点时间就可以了。

阅读全文
0 0