Spring Security 从配置入门 学习讲解。刽子手------------securityConfig.xml

不知道我的web.xml 大家都理解了没。  废话确实有点多,可能很多知识点,大家都知道,可是我学的时候,压根什么都不懂啊....

这篇我们要讲刽子手  securityConfig。 为什么要说他是刽子手呢?  因为他是无良掌柜的小工,直接的操盘手......

<?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"
    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-3.0.xsd">
    <http access-denied-page = "/accessDenied.jsp">        <!-- 访问拒绝页面 -->
        <form-login login-page="/login.jsp"/>   <!-- 定义登陆界面 -->
        <intercept-url pattern="/login.jsp" filters="none"/>
        <session-management>
            <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>  <!-- 用户最大登录数设置为1 ,超过则引发异常 -->
        </session-management>
        <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>  <!-- 自定义FILTER ,FilterSecurityInterceptor 负责授权-->
    </http>
    <!-- myFilter -->
    <beans:bean id = "myFilter" class = "com.qbt.spring.security.MyFilterSecurityInterceptor">
            <beans:property name="authenticationManager" ref ="authenticationManager"></beans:property>  <!-- 登陆验证 ,验证你的用户名密码噼里啪啦-->
            <beans:property name="securityMetadataSource" ref = "securityMetadataSource"></beans:property>  <!-- 资源数据源的定义 ,神马权限对应神马资源 噼里啪啦-->
             <beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean"></beans:property>  <!-- 访问决策 有没有权限访问资源 噼里啪啦-->
    </beans:bean>                                                 

    <!-- 验证配置,认证管理器,实现UserDetailService接口 -->
    <!-- authenticationManager 可以有多个provider提供信息,我们用myUserDetailService获取信息 -->
    <!-- Spring Security中进行身份验证的是AuthenticationManager接口,ProviderManager是它的一个默认实现,
        但它并不用来处理身份认证,而是委托给配置好的AuthenticationProvider,每个AuthenticationProvider会轮流检查身份认证。
        检查后或者返回Authentication对象或者抛出异常 -->
    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="myUserDetailService"></authentication-provider>
    </authentication-manager>

    <!-- 获取user数据,可以从数据库中获取用户密码,角色等! -->
    <beans:bean id = "myUserDetailService" class = "com.qbt.spring.security.MyUserDetailService"></beans:bean>

    <!-- 访问决策器,决定用户的角色,访问的权限 -->
    <beans:bean id = "myAccessDecisionManagerBean" class = "com.qbt.spring.security.MyAccessDecisionManager"></beans:bean>

    <!-- 资源数据源的定义 什么资源对应什么权限,或者什么资源能被什么角色访问-->
    <beans:bean id = "securityMetadataSource" class = "com.qbt.spring.security.MyInvocationSecurityMetadataSource"></beans:bean>

</beans:beans>

可以看出来,这里的核心过滤器是我们自己实现的MyFilterSecurityInterceptor

//下面,贴出他的代码

package com.qbt.spring.security;

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;

/**
 *登陆后,每次访问资源都会访问这个拦截器 ,执行doFilter, 调用invoke,
 *super.beforeInvocation() 会调用SecurityMetadataSource 的getAttributes方法获取fi对应的所有权限,再调用decide的方法,判断是否有权限
 *然后调用下一个拦截器。
 *
 */
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{

    //配置文件注入
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    //登陆后,每次访问资源都通过这个拦截器拦截
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    private void invoke(FilterInvocation fi) throws IOException, ServletException {
        //fi 封装了request,response,chain
        //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
        //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够

        InterceptorStatusToken token = super.beforeInvocation(fi);

        //这里的token里面到底是什么呢?  八一八 来!
        /**
         * 源码---
         *  protected InterceptorStatusToken beforeInvocation(Object object)
            {
                Assert.notNull(object, "Object was null");   //预言
                //省略
                Collection attributes = obtainSecurityMetadataSource().getAttributes(object);
                //调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
                if(attributes == null)
                {
                    //抛异常省略
                    return null;
                }
                //省略
                //判断是否需要对认证实体重新认证,默认为否
                Authentication authenticated = authenticateIfRequired();
                try
                {//decide方法来校验用户的权限是否足够
                    accessDecisionManager.decide(authenticated, object, attributes);
                }
                catch(AccessDeniedException accessDeniedException)
                {//抛异常
                    publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException));
                    throw accessDeniedException;
                }
                //省略
                return new InterceptorStatusToken(authenticated, true, attributes, object);
            }
         *
         */
        try{
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally{
            super.afterInvocation(token, null);
        }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {

    }
    //相对应的MyAccessDecisionManager的supports方法必须放回true,否则会提醒类型错误
    @Override
    public Class<? extends Object> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource  newSource  ){
        this.securityMetadataSource = newSource;
    }

    @Override
    public void destroy() {

    }
}

//累啊...

然后我们来看看 authenticationManager

package com.qbt.spring.security;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
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{

    /**
     * 获取用户信息,返回User放到Spring的全局缓存SecurityContentHolder中,让其他过滤器使用
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
        UserDetails user = null;
        Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>(); //定义一个权限集合
        GrantedAuthorityImpl auth1 = new GrantedAuthorityImpl("ROLE_ADMIN");  //定义一个管理员权限
        GrantedAuthorityImpl auth2 = new GrantedAuthorityImpl("ROLE_USER");   //定义一个用户权限
        System.out.println("**********MyUserDetailService登陆验证,通过用户名获取权限************");
        if(username.equals("admin")){
            auths.add(auth2);
            auths.add(auth1);
        }else{
            auths.add(auth2);
        }
        // username, password, enable, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities
        user = new User(username, "admin", true, true, true, true, auths);
        return user;
    }

}

然后再看看MyInvocationSecurityMetadataSource

package com.qbt.spring.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

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 org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;

/**
 * @author ORC
 * FilterInvocationSecurityMetadataSource  继承于 SecurityMetadataSource
 *
 */

public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private UrlMatcher urlMatcher = new AntUrlPathMatcher();
    public static Map<String,Collection<ConfigAttribute>> resourceMap = null;

    public MyInvocationSecurityMetadataSource () {
        loadResourceDefine();
    }

    /**
     * 获取角色和资源的相对应的关系,在tomcat启动时加载。
     * 因为是在tomcat启动时就要加载,所以如果权限对应关系改变的话,就需要重新获取
     * 如果要调用Dao的话,但是在启动的时候,这个Dao可能还没有加载
     * 所以如果要在这里调用数据库的话,要自己写sessionfaction,sql/hql
     * 还有一种方法就是,在getAttributes方法里调用Dao,因为这个方法是在tomcat启动之后才调用的
     */
    private void loadResourceDefine() {
        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 no = new SecurityConfig("ROLE_NO");
        attsNo.add(no);
        resourceMap.put("/other.jsp", attsNo);

        System.out.println("*************MyInvocationSecurityMetadataSource 调用,获取角色和资源对应值*******************");
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object obj) throws IllegalArgumentException {
        String url = ((FilterInvocation)obj).getRequestUrl();
        System.out.println("*************MyInvocationSecurityMetadataSource getAttribute调用,获取资源所对应的角色集合*******************");
        Iterator<String>ite = resourceMap.keySet().iterator();
        while(ite.hasNext()){
            String resUrl = ite.next();
            if(urlMatcher.pathMatchesUrl(resUrl, url)) {
                return resourceMap.get(resUrl);
            }
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> class1) {
        return true;
    }

}

再看MyAccessDecisionManager

package com.qbt.spring.security;

import java.util.Collection;
import java.util.Iterator;

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;

public class MyAccessDecisionManager implements AccessDecisionManager{

    @Override
    public void decide(Authentication authentication, Object obj, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if(configAttributes == null) {
            return;
        }
        System.out.println("*************MyAccessDecesionManager 判断用户是否有权限**************");
        Iterator<ConfigAttribute> itr = configAttributes.iterator();
        while(itr.hasNext()){
            ConfigAttribute ca = itr.next();
            String needRole = ((SecurityConfig)ca).getAttribute();
            for(GrantedAuthority ga : authentication.getAuthorities()){
                if(needRole.equals(ga.getAuthority())){
                    return;
                }
            }
        }
        throw new AccessDeniedException("No Right");
    }

    //这个 supports(ConfigAttribute) 方法在启动的时候被
    //AbstractSecurityInterceptor调用,来决定AccessDecisionManager
    //是否可以执行传递ConfigAttribute
    @Override
    public boolean supports(ConfigAttribute configattribute) {
        return false;
    }
    //supports(Class)方法被安全拦截器实现调用,
    //包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型。
    @Override
    public boolean supports(Class<?> class1) {
        return true;
    }

}
时间: 2024-11-12 20:55:51

Spring Security 从配置入门 学习讲解。刽子手------------securityConfig.xml的相关文章

Spring Security 从配置入门 学习讲解。万恶之源------------web.xml

这段时间,工作闲了下来,接触了Spring Security,对于我一个基础很差的人来说,无疑是个挑战啊. 经过一段时间的摸索,终于有了点眉目,在这里,要特别感谢http://blog.csdn.net/u012367513/article/details/38866465 二当家的 博文对我的帮助.我的代码也是在他的基础上整理而来,只是增加了自己的一些见解. 再次感谢他的帮助. 我的基础很是薄弱,最然 二当家的 博文中已经讲解的很是清楚,但是我还是希望自己能过上一遍. 本文适宜读者: Spri

spring security中配置密码为md5的带salt加密

spring security中配置密码为md5的带salt加密 service: private Md5PasswordEncoder encoder; //spring security md5 public Md5PasswordEncoder getEncoder() { return encoder; } @Resource public void setEncoder(Md5PasswordEncoder encoder) { this.encoder = encoder; } @O

spring的Java配置入门(Spring Boot学习之一)

spring的Java配置 1.创建maven项目 使用idea创建maven项目,这里顺便提一下,idea真的比eclipse好用,早点熟悉吧.然后就是maven是java项目管理最主流的工具,自己先配置一个试试,很好上手. 2.导入依赖 编辑pom文件 1.首先配置一下jdk版本和编码,两种方式,按道理任意一种都可以,出问题的话两种都加上吧,如下 <build>        <finalName>spring-learn</finalName>        &l

Spring Security安全框架入门篇

一.Spring Security相关概念 1.1..Spring Security介绍: Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全訪问控制解决方式的安全框架(简单说是对訪问权限进行控制嘛).它提供了一组能够在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能.为应用系统提供声明式的安

Spring Security Web应用入门环境搭建

在使用Spring Security配置Web应用之前,首先要准备一个基于Maven的Spring框架创建的Web应用(Spring MVC不是必须的),本文的内容都是基于这个前提下的. pom.xml添加依赖 除了Spring框架本身的一些依赖包,还需要在pom.xml中添加Spring Security的依赖包: <dependency> <groupId>org.springframework.security</groupId> <artifactId&g

spring boot跨域请求访问配置以及spring security中配置失效的原理解析

一.同源策略 同源策略[same origin policy]是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源. 同源策略是浏览器安全的基石. 什么是源 源[origin]就是协议.域名和端口号.例如:http://www.baidu.com:80这个URL. 什么是同源 若地址里面的协议.域名和端口号均相同则属于同源. 是否是同源的判断 例如判断下面的URL是否与 http://www.a.com/test/index.html 同源 http://www.a

Spring Security 4 自定义登录表单 注解和XML例子(带源码)

上一篇文章: Spring Security 4 Hello World 基于注解 和 XML 例子 下一篇:Spring Security 4 退出 示例 原文地址:http://websystique.com/spring-security/spring-security-4-custom-login-form-annotation-example/ [已翻译文章,点击分类里面的spring security 4查看.] [ 翻译by 明明如月 QQ 605283073] 本文演示Sprin

Spring Security教程(一)

一 概要 Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架.它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权.这里过多的spring security解释和作用就不在这里赘述了,请自行搜索.目前最新版本的Spring Security为4.2.2,但是我这里用了稳定版本3.1.3.下面例子为一个简单的Spring Security配置应用. 二 新建一个web maven项目 如果不知道怎么新建web mav

springboot学习总结(八)Spring security配置

(一)配置类 Spring security的配置和Spring MVC的配置类似,只需在一个配置类上注解@EnableWebSecurity(Springboot项目可以不用),并让这个类继承WebSecurityConfigurerAdapter. @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected