首先,希望还对 spring-security框架完全不懂的新手 下载下Git源码。 引入到项目中。这个短文就是边看源码边聊的。也会启动下项目验证自己的推想。
spring-security 登陆认证的配置项如下:
<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, 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 。 本来不想贴代码的。更想愿意读的人自己下载源码自己看。
登陆:
UsernamePasswordAuthenticationFilter.attemptAuthentication() 方法会从request中获取 name password
然后构造一个 new UsernamePasswordAuthenticationToken(password,username)
然后调用接口 AuthenticationManager.authenticate() (认证管理类中的一个实现类 ProviderManager的认证方法)
我们是这样配置认证管理类 AuthenticationManager 的, 给它提供了一个 user-service bean 的id ,这个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>
其实很少有人这么傻着从校验文件 去查属性的。 除了像我这种机智到二的人。 其实官方文档说的很清楚。但是我抱着探究的态度懒得查。
密码校验通过。就可以了。
很多时候,我们希望做更多的扩展、比如加一些U盾之类的口令啦、短信校验啦。验证码啦。 那么要实现、可以加些自定义的过滤器,也可以重写一些方法,等等、初次探究,我现在还不够清楚。 不过这些都略有些麻烦。
其实如果你自己去校验用户。然后将用户登录信息放入SecurityContext 里面 也就可以随心所欲了。
如下图。上面未截图部分自己校验用户、验证码、尝试次数等…
关键代码 Authentication auth = authenticationManager.authenticate(authRequest);
@Resource(name = "authenticationManager")
private AuthenticationManager authenticationManager = null;
注入authenticationManager的 其实就是之前写到的 ProviderManager 他走了下面的方法。 由于没有配置userName 参数 使用的是默认的j_username
自然也是取不到值的。
有空继续