zoukankan      html  css  js  c++  java
  • spring-security 登陆认证之初次探究

    首先,希望还对 spring-security框架完全不懂的新手 下载下Git源码。 引入到项目中。这个短文就是边看源码边聊的。也会启动下项目验证自己的推想。

    一、登陆认证的登陆配置项

    <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" default-target-url="/index.ht" 
      username-parameter="username" password-parameter="password" login-processing-url="/j_spring_security_check"/> 
    <logout logout-url="/logout.ht"/>

    看到这个配置,其实就大略明白了。 这就像配置了一个control/Servlet, userName 参数名字为 ”name“,password 为”password“

    然后校验用户密码,通过就跳转的页面为 index.ht 。 

    
    
    

    spring-security框架维护了一个过滤器链来提供服务, 而<form-login/> 这个登陆配置项其实创建了一个名为UsernamePasswordAuthenticationFilter的过滤器 。

    框架提供的这些过滤器,也包括<custom-filter/>配置的过滤器。都是通过假名有严格顺序来执行的。稍后详细介绍自定义过滤器。

    UsernamePasswordAuthenticationFilter : 

    正如我们配置的这些参数,也会有默认配置的   比如

    usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY =“j_username”,

    passwordParameter=“j_password”

    默认接受form请求地址 :j_spring_security_check ,

    这些可配置的参数,都会存在默认参数。这些参数的读取是在 Initializing Spring root WebApplicationContext 后,加载并且解析xml配置文件。然后初始化ioc 容器。形成上面所提的过滤器链。

    二、简单说一下解析xml 过程

    HttpSecurityBeanDefinitionParser.parse() {  filterChains.add(createFilterChain(element, pc)); }

    createFilterChain方法会调用 AuthenticationConfigBuilder的构造方法 初始化各种filter        createFormLoginFilter(sessionStrategy, authenticationManager); 即为登录配置信息xml的解析处理方法:

    SecurityNamespaceHandler.parse(Element element, ParserContext pc)  //
    
    关键代码:
    
    String name = pc.getDelegate().getLocalName(element); 
    BeanDefinitionParser parser = parsers.get(name); 
    通过配置项的名字。以策略模式获取到专用解析器 都实现自BeanDefinitionParser 接口, 通过父类的引用执行子类的具体实现。调用这些子类的parse()方法eg:RememberMeBeanDefinitionParser,LogoutBeanDefinitionParser等、、
    错误代码不必看

    <form-login/> 的解析在 FormLoginBeanDefinitionParser,  解析后拿到配置的参数,然后初始化一个filter 。

    不知道这个解析方法为啥没有实现BeanDefinitionParser 。 本来不想贴代码的。更想愿意读的人自己下载源码自己看。

    xml解析地方

    三、获取登陆获取账号密码

    切面走到这个登陆过滤器的时候   UsernamePasswordAuthenticationFilter.attemptAuthentication()  的方法    会从request中,通过配置的userNameParam ,passwordParam 获取 name password

    然后构造一个包装了密码账号的对象: new UsernamePasswordAuthenticationToken(password,username)

    然后调用接口 AuthenticationManager.authenticate() (认证管理类中的一个实现类 ProviderManager的认证方法

    四、配置验证密码的认证管理类

    配置认证管理类 AuthenticationManager 需要 给它提供了一个 user-service  bean    来通过用户名获取用户 

    这个userDetailProvider bean 需要实现 UserDetailsService接口 提供一个 loadUserByUsername()方法。

    配置项:

    <security:authentication-manager alias="authenticationManager">
    <security:authentication-provider user-service-ref="userDetailProvider"/> 
    </security:authentication-manager>
    <bean id="userDetailProvider" class="com.hotent.web.security.provider.UserAuthProvider"/> 

    然后 从ProviderManager 中的  List<AuthenticationProvider> providers认证策略都  拿出来 进行认证(虚)

    AbstractUserDetailsAuthenticationProvider .authenticate()

              retrieveUser() // 调用子类DaoAuthenticationProvider的实现方法

    DaoAuthenticationProvider.retrieveUser() 会通过我之前配置的userDetailProvider.loadUserByUsername(username),获取用户,

    然后preAuthenticationChecks.check(user);  校验用户是否可用、锁定、过期

    然后调用additionalAuthenticationChecks()方法验证密码。 

     五、配置 密码加密方式

    接着我登陆不上才发现没有配置密码的加密类型。随便找了 个文档。配了下、居然发现启动不了,妹的。

    还好我比较机智,找到了xsd校验文件

    顺利找到了正确配置方法,在authentication-provider    element下、 有一个password-encoder   xs:element

    这个element 有个attribute<xs:attributeGroup ref="security:password-encoder.attlist"/>,这个想必就是spring-security所支持的所有加密类型了。那xml 就改成了 这样

    最终authenticationManager的配置  如下

    <security:authentication-manager alias="authenticationManager"><!-- 鉴定管理类 -->  
            <security:authentication-provider  user-service-ref="userDetailProvider"> 
                <security:password-encoder hash="sha-256"/> 
            </security:authentication-provider>  
        </security:authentication-manager>

    其实很少有人这么傻着从校验文件 去查属性的。 除了像我这种机智到二的人。 其实官方文档说的很清楚,而且xml也会有提示。但是我抱着探究的态度还是直接看xsd。

    这样密码加密校验就通过。

    接着将成功登陆用户,和用户信息发布出一个事件

     applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent(authentication));

    ///

    六、登陆的扩展

    很多时候,我们希望做更多的扩展、比如加一些U盾之类的口令啦、短信校验啦。验证码啦。 那么要实现、可以加些自定义的过滤器,也可以重写一些方法,等等、初次探究,我现在还不够清楚。  不过这些都略有些麻烦。

    其实如果你自己去校验用户。然后将用户登录信息放入SecurityContext 里面 也就可以随心所欲了。

    如下图   验证账号密码、验证码 的截图略过   !直接是验证密码后的操作。

    关键代码  

      UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, encrptPassword);
      authRequest.setDetails(new WebAuthenticationDetails(request));
      SecurityContext securityContext = SecurityContextHolder.getContext();
      Authentication auth = authenticationManager.authenticate(authRequest); 
      securityContext.setAuthentication(auth);
      sessionStrategy.onAuthentication(auth, request, response);   

    注入要使用的类

      @Resource(name = "authenticationManager")
        private AuthenticationManager authenticationManager = null;
        @Resource
        UserDetailsService userDetailsService;
        @Resource
        private SessionAuthenticationStrategy sessionStrategy= new NullAuthenticatedSessionStrategy();

    注入的authenticationManager 其实就是之前写到的  ProviderManager

    我们直接使用用户名,密码(加密后) 构建了UsernamePasswordAuthenticationToken(为存放密码账号信息的一个令牌) 是Authentication接口的一个实例。然后把构建好的  authentication 发送给authenticationManager 进行校验 。

    authenticationManager 成功校验后,返回一个完全的 Authentication 实例。

    SecurityContextHolder.getContext().setAuthentication(...)

    Spring Security 并不知道你怎么把Authentication 对象 放到了SecurityContextHolder 里面,唯一关键点就是 “SecurityContextHolder 已经包含了一个 Authentication 标识了一个主体”。  这样就能满足AbstractSecurityInterceptor 之前的验证一个用户需要。

     所以、这个authentication放入SecurityContextHolder的过程可以有很多种方式、一个过滤器、一个control方法、就能达到目的,自然就实现了登陆。

    有空继续

  • 相关阅读:
    gdb 查看变量~p长串末尾省略号, 一个页面显示不完
    Git 在团队中的最佳实践--如何正确使用Git Flow[转]
    apktool+dex2jar+xjad反编译android程序
    浏览器缓存详解:expires,cache-control,last-modified,etag详细说明
    64位windows 7下成功配置TortoiseGit使用Github服务器
    github简单使用教程
    浅淡HTML5移动Web开发
    Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系
    深入浅出Symfony2
    利用PHP取二进制文件头判断文件类型
  • 原文地址:https://www.cnblogs.com/javaMan/p/4204947.html
Copyright © 2011-2022 走看看