1.spring security初体验
applicationContext.xml
<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-2.5.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <http auto-config='true'> <intercept-url pattern="/**" access="ROLE_USER" /> </http> <authentication-provider> <user-service> <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" /> <user name="bob" password="bobspassword" authorities="ROLE_USER" /> </user-service> </authentication-provider> </beans:beans>
web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <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> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <!-- <param-value>classpath*:applicationContext.xml</param-value> --> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
auto-config属性其实是<form-login/>,<http-basic/>,<logout/>的缩写.
没配置登录页面的情况下,spring自动生成一个.
2.指定自己的登录页面以及跳转规则
<http> <intercept-url pattern='/login.htm*' filters='none'/> <intercept-url pattern='/**' access='ROLE_USER' /> <form-login login-page='/login.htm' default-target-url='/home.htm' always-use-default-target='true' /> </http>
<intercept-url pattern='/login.htm*' filters='none'/>这个标签是把验证排除在外。因为这是一个登录页面。
而form-login标签的login-page则指定遇到受限资源需要进行登录时候的页面.
default-target-url指定了从登录页面登录后进行跳转的页面
always-use-default-target这个属性表示登录成功后强制跳转到default-target-url这个地址。默认情况下如果是从首先页面过来登录的,那么跳转是跳转回受限页面。
3.其他的验证方式.
LDAP验证
<authentication-provider user-service-ref='myUserDetailsService'/>
DATABASE验证
<authentication-provider> <jdbc-user-service data-source-ref="securityDataSource"/> </authentication-provider>
或者
<authentication-provider user-service-ref='myUserDetailsService'/> <beans:bean id="myUserDetailsService" class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl"> <beans:property name="dataSource" ref="dataSource"/> </beans:bean>
4.密码的编码器配置
<authentication-provider> <password-encoder hash="sha"/> <user-service> <user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f" authorities="ROLE_USER, ROLE_ADMIN" /> <user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f" authorities="ROLE_USER" /> </user-service> </authentication-provider>
另外还以为密码配置种子(盐值)
<password-encoder hash="sha"> <salt-source user-property="username"/> </password-encoder>
还可以通过password-encoder的ref属性,指定一个自定义的密码编码器bean。 这应该包含application context中一个bean的名字,它应该是Spring Security的PasswordEncoder接口的一个实例。
5.对HTPPS的处理
<http> <intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/> <intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/> ... </http>
当用户用http访问需要https的访问验证时可以通过requires-channel属性来进行跳转.
requires-channel共有三个值:http,https,any。 any表示http和https都可以。
如果使用了非默认的http和https的端口号,那么添加port-mappings即可
<http> ... <port-mappings> <port-mapping http="9080" https="9443"/> </port-mappings> </http>
6.限制用户的登录(单次登录)
在web.xml中添加监听器
<listener> <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class> </listener>
然后再配spring置中添加
<http> ... <concurrent-session-control max-sessions="1" /> </http>
这种做法是在第二次登陆时,第一登录就失效了。
如果想禁止第二次登陆,那么可以设置exception-if-maximum-exceeded.
<http> ... <concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true"/> </http>
字面上不难理解,第二次登陆就抛出异常,这样就防止了重复登录
7.OpenID登录
命名空间支持OpenID登录,替代普通的表单登录,或作为一种附加功能,只需要进行简单的修改:
<http> <intercept-url pattern="/**" access="ROLE_USER" /> <openid-login /> </http>
你应该注册一个OpenID供应器(比如myopenid.com),然后把用户信息添加到你的内存<user-service>中:
<user name="http://jimi.hendrix.myopenid.com/" password="notused" authorities="ROLE_USER" />
你应该可以使用myopenid.com网站登录来进行验证了。
8.添加自己的过滤器
添加你自己的filter
如果你以前使用过Spring Security,你就应该知道这个框架里维护了一个过滤器链,来提供服务。 你也许想把你自己的过滤器添加到链条的特定位置,或者使用一个Spring Security的过滤器,这个过滤器现在还没有在命名空间配置中进行支持(比如CAS)。 或者你想要使用一个特定版本的标准命名空间过滤器,比如<form-login>创建的AuthenticationProcessingFilter,从而获得一些额外的配置选项的优势,这些可以通过直接配置bean获得。 你如何在命名空间配置里实现这些功能呢?过滤器链现在已经不能直接看到了。
过滤器顺序在使用命名空间的时候是被严格执行的。 每个Spring Security过滤器都实现了Spring的Ordered接口,这些通过命名空间穿件的过滤器在初始化的时候就预先被排好序了。 标准的过滤器在命名空间里都有自己的假名,有关创建过滤器的过滤器,假名和命名空间元素,属性可以在Table 2.1, “标准过滤器假名和顺序”中找到。
假名 | 过滤器累 | 命名空间元素或属性 |
CHANNEL_FILTER | ChannelProcessingFilter | http/intercept-url |
CONCURRENT_SESSION_FILTER | ConcurrentSessionFilter | http/concurrent-session-control |
SESSION_CONTEXT_INTEGRATION_FILTER | HttpSessionContextIntegrationFilter | http |
LOGOUT_FILTER | LogoutFilter | http/logout |
X509_FILTER | X509PreAuthenticatedProcessigFilter | http/x509 |
PRE_AUTH_FILTER | AstractPreAuthenticatedProcessingFilter | Subclasses N/A |
CAS_PROCESSING_FILTER | CasProcessingFilter | N/A |
AUTHENTICATION_PROCESSING_FILTER | AuthenticationProcessingFilter | http/form-login |
BASIC_PROCESSING_FILTER | BasicProcessingFilter | http/http-basic |
SERVLET_API_SUPPORT_FILTER | SecurityContextHolderAwareRequestFilter | http/@servlet-api-provision |
REMEMBER_ME_FILTER | RememberMeProcessingFilter | http/remember-me |
ANONYMOUS_FILTER | AnonymousProcessingFilter | http/anonymous |
EXCEPTION_TRANSLATION_FILTER | ExceptionTranslationFilter | http |
NTLM_FILTER | NtlmProcessingFilter | N/A |
FILTER_SECURITY_INTERCEPTOR | FilterSecurityInterceptor | http |
SWITCH_USER_FILTER SwitchUserProcessingFilter N/A
你可以把你自己的过滤器添加到队列中,使用custom-filter元素,使用这些名字中的一个,来指定你的过滤器应该出现的位置:
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter"> <custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/> </beans:bean>
你还可以使用after 或 before属性,如果你想把你的过滤器添加到队列中另一个过滤器的前面或后面。 可以分别在position属性使用"FIRST" 或 "LAST"来指定你想让你的过滤器出现在队列元素的前面或后面。
避免过滤器位置发生冲突
如果你插入了一个自定义的过滤器,而这个过滤器可能与命名空间自己创建的标准过滤器放在同一个位置上,这样首要的是你不要错误包含命名空间的版本信息。 避免使用auto-config属性,然后删除所有会创建你希望替换的过滤器的元素。
注意,你不能替换那些<http>元素自己使用而创建出的过滤器,比如HttpSessionContextIntegrationFilter, ExceptionTranslationFilter 或 FilterSecurityInterceptor。
如果你替换了一个命名空间的过滤器,而这个过滤器需要一个验证入口点(比如,认证过程是通过一个未通过验证的用户访问受保护资源的尝试来触发的),你将也需要添加一个自定义的入口点bean。
9.防止session固定攻击。
<http>中的session-fixation-protection属性配置可以防止这种攻击。
它有三个选项:
a.migrateSession - 创建一个新session,把原来session中所有属性复制到新session中。这是默认值。
b.none - 什么也不做,继续使用原来的session。
c.newSession - 创建一个新的“干净的”session,不会复制session中的数据。
10.方法的保护
a.
<global-method-security secured-annotations="enabled" jsr250-annotations="enabled"/>
这个标签用于开启Spring Security的@Secured和JSR-250注解功能
public interface BankService { @Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account readAccount(Long id); @Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account[] findAccounts(); @Secured("ROLE_TELLER") public Account post(Account account, double amount); }
b.使用protect-pointcut来添加安全切点
<global-method-security> <protect-pointcut expression="execution(* com.mycompany.*Service.*(..))" access="ROLE_USER"/> </global-method-security>
c.使用intercept-methods来构建安全方法
<bean:bean id="target" class="com.mycompany.myapp.MyBean"> <intercept-methods> <protect method="set*" access="ROLE_ADMIN" /> <protect method="get*" access="ROLE_ADMIN,ROLE_USER" /> <protect method="doSomething" access="ROLE_USER" /> </intercept-methods> </bean:bean>
11.自定义的AccessDecisionManager
方法安全的配置
<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean"> ... </global-method-security>
web安全的配置
<http access-decision-manager-ref="myAccessDecisionManagerBean"> ... </http>
12.默认的验证管理器
如果你在命名空间中使用了HTTP或方法安全,你不能使用自定义的AuthenticationManager.
ProviderManager注册另外的AuthenticationProvider bean,你可以使用<custom-authentication-provider>元素实现
<bean id="casAuthenticationProvider" class="org.springframework.security.providers.cas.CasAuthenticationProvider"> <security:custom-authentication-provider /> ... </bean>
另一个常见的需求是,上下文中的另一个bean可能需要引用AuthenticationManager。 这里有一个特殊的元素,可以让你为AuthenticationManager注册一个别名,然后你可以application context的其他地方使用这个名字。
<security:authentication-manager alias="authenticationManager"/> <bean id="customizedFormLoginFilter" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter"> <security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/> <property name="authenticationManager" ref="authenticationManager"/> ... </bean>
13.从任意位置获取用户名密码等信息.
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (obj instanceof UserDetails) { String username = ((UserDetails)obj).getUsername(); } else { String username = obj.toString(); }