在需求上后台管理系统 需要有一个登陆页,同时手机端也允许普通用户登录。
这个时候我设置两个http过滤器
<http auto-config="false" pattern="/phone" entry-point-ref="authenticationEntryPoint">
<csrf disabled="true"/>
<custom-filter ref="myLoginFilter" position="FORM_LOGIN_FILTER" />
<logout logout-success-url="/login"/>
</http>
<http auto-config="true" >
<access-denied-handler error-page="/accessDenied"/>
<intercept-url pattern="/phone" access="permitAll" />
<form-login login-page="/login.jsp"
always-use-default-target="true" authentication-failure-url="/login.jsp?error=true"
default-target-url="/login" login-processing-url="/login_user" />
<csrf disabled="true"/>
<anonymous enabled="false"/>
<!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了, 这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->
<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
</http>
这个时候FilterchanProxy里的filterchains 里会增加两个filter,一个的pattern 是/phone 一个的pattern的anyrequest。
提示:需要把anyrequest的放置在chains的末尾,也就是xml配置的时候置于最后。不然启动会报错。
<b:bean id="myLoginFilter" class="com.kaqm.action.manage.MyUsernamePasswordAuthenticationFilter">
<b:property name="authenticationManager" ref="authenticationManager"/>
<b:property name="authenticationFailureHandler" ref="failureHandler"/>
<b:property name="filterProcessesUrl" value="/phone"/>
<b:property name="authenticationSuccessHandler" ref="successHandler"/>
</b:bean>
<b:bean id="authenticationEntryPoint"
class="com.kaqm.action.manage.MyAuthenticationEntryPoint">
<b:property name="loginFormUrl" value="/login.html" />
</b:bean>
<!-- end -->
<!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
我们的所有控制将在这三个类中实现,解释详见具体配置 -->
<b:bean id="myFilter" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<b:property name="authenticationManager"
ref="authenticationManager" />
<b:property name="accessDecisionManager"
ref="myAccessDecisionManager" />
<b:property name="securityMetadataSource"
ref="mySecurityMetadataSource" />
</b:bean>
<!-- <b:bean id="acquireResourceDBInterceptor" class="com.util.security.interceptor.AcquireResourceDBInterceptor">
</b:bean>
<b:bean id="acquireResource" class="com.util.security.web.LeftAcquireResourceImpl">
</b:bean> -->
<!-- 验证配置 , 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="md5">
</password-encoder>
</authentication-provider>
<!-- <authentication-provider>
<user-service>
<user name="zhangsan" password="zhangsan" authorities="ROLE_NORMAL"/>
</user-service>
</authentication-provider> -->
</authentication-manager>
<!-- 项目实现的用户查询服务,将用户信息查询出来 -->
<b:bean id="userDetailsService" class="com.util.security.support.MyUserDetailService" />
<!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
<b:bean id="myAccessDecisionManager"
class="com.util.security.support.MyAccessDecisionManager">
</b:bean>
<!-- 资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->
<b:bean id="mySecurityMetadataSource"
class="com.util.security.support.MyInvocationSecurityMetadataSourceService">
</b:bean>
这里我自己写了一个authenticationEntryPoint类 实现类为com.kaqm.action.manage.MyAuthenticationEntryPoint代码均与LoginUrlAuthenticationEntryPoint相同,只是多了一个无参构造函数
因为系统启动的时候总是提示我LoginUrlAuthenticationEntryPoint缺少一个无参数构造函数.
spring security的用户名密码验证是在MessageDigestPasswordEncoder.isPasswordValid 进行校验的。
具体流程为UsernamePasswordAuthenticationFilter 对request中的用户名密码进行提取 封装成UsernamePasswordAuthenticationToken对象,
调用AuthenticationManager 一般是ProviderManager.authenticate
调用DaoAuthenticationProvider.authenticate
调用DaoAuthenticationProvider.retriveUser
调用UserDetailService.loadUserByUsername(username)获取用户名 用户名匹配
调用DaoAuthenticationProvider.addtionalAuthenticationChekcs(user,(UsernamePasswordAuthenticationToken) authentication);
调用Md5PasswordEncoder的父类MessageDigestPassword.Encoder.isPasswordValid(String encPass,String rawPass,ObjectSalt)进行密码匹配