目录
SpringSecurity3.X--一个简单实现
SpringSecurity3.X--前台与后台登录认证
SpringSecurity3.X--remember-me
SpringSecurity3.X--验证码
作者对springsecurity研究不深,算是个初学者吧,最近很不完整的看了一下翻译的很是生硬的《Spring3Security-3.0.1中文官方文档.pdf》,为了便于学习和记忆,所以将所学知识在此记录下来。
这里给出一个简单的安全验证的实现例子,先说一下需求:
1.通过登录页面进行登录
2.用户登录前访问被保护的地址时自动跳转到登录页面
3.用户信息存储在数据表中
4.用户权限信息存在在数据表中
5.用户登录成功后访问没有权限访问的地址时跳转到登录页面
ok,以上就是一个基本的需求了,大部分的系统都是基于该需求实现登录模块的。
给出实现之前,先简单说明一下springsecurity的原理,
1.AccessDecisionManager
和我们一般实现登录验证采用filter的方式一样,springsecurity也是一个过滤器,当请求被springsecurity拦截后,会先对用户请求的资源进行安全认证,如果用户有权访问该资源,则放行,否则将阻断用户请求或提供用户登录,
在springsecurity中,负责对用户的请求资源进行安全认证的是AccessDecisionManager,它就是一组投票器的集合,默认的策略是使用一个AffirmativeBased,既只要有一个投票器通过验证就允许用户访问,
所以如果希望实现自己的权限验证策略,实现自己的投票器是一个很好的选择。
2.UserDetailsService
如果用户没有登录就访问某一个受保护的资源,则springsecurity会提示用户登录,用户登录后,由UserDetailsService来验证用户是否合法,既验证用户名和密码是否正确,同时验证用户是否具备相应的资源权限,
即对应的access的value。
如果用户验证通过,则由AccessDecisionManager来决定是否用户可以访问该资源。
下面给出具体实现:
web.xml
基本上都是这样配置,就不废话了。
- <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>
<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>
applicationContext-security.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
- xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc"
- xmlns:tool="http://www.springframework.org/schema/tool" xmlns:beans="http://www.springframework.org/schema/beans"
- xsi:schemaLocation="
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
- http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
- http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
- http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"
- default-lazy-init="true">
- <!-- 不需要进行认证的资源,3.0之后才改为这样配置 -->
- <http security="none" pattern="/index.do" />
- <!-- 因为要使用自己的权限验证规则,所以这里要配置access-decision-manager-ref
- 实际上,我只是在accessDecisionManager中增加了一个投票器,其它的属性都比较简单,不多说了 -->
- <http auto-config='true' access-decision-manager-ref="accessDecisionManager"
- access-denied-page="/index.do">
- <intercept-url pattern="/demo.do*" access="IS_AUTHENTICATED_REMEMBERED" />
- <intercept-url pattern="/**/*.do*" access="HODLE" />
- <logout logout-url="/logout.do" invalidate-session="true"
- logout-success-url="/logout.jsp" />
- <form-login login-page="/index.do" default-target-url="/frame.do"
- always-use-default-target="true" authentication-failure-url="/index.do?login_error=1" />
- <session-management>
- <concurrency-control max-sessions="1" />
- </session-management>
- </http>
- <!-- Automatically receives AuthenticationEvent messages -->
- <beans:bean id="loggerListener"
- class="org.springframework.security.authentication.event.LoggerListener" />
- <!-- 认证管理器,使用自定义的UserDetailsService,并对密码采用md5加密-->
- <authentication-manager>
- <authentication-provider user-service-ref="userService">
- <password-encoder hash="md5" />
- </authentication-provider>
- </authentication-manager>
- <beans:bean id="userService" class="com.piaoyi.common.security.UserService" />
- <!-- 访问决策管理器,这里使用AffirmativeBased,并加入一个自定义的投票器DynamicRoleVoter -->
- <beans:bean id="accessDecisionManager"
- class="org.springframework.security.access.vote.AffirmativeBased">
- <beans:property name="decisionVoters">
- <beans:list>
- <beans:bean class="org.springframework.security.access.vote.RoleVoter" />
- <beans:bean
- class="org.springframework.security.access.vote.AuthenticatedVoter" />
- <beans:bean class="com.piaoyi.common.security.DynamicRoleVoter" />
- </beans:list>
- </beans:property>
- </beans:bean>
- </beans:beans>
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tool="http://www.springframework.org/schema/tool" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd" default-lazy-init="true"> <!-- 不需要进行认证的资源,3.0之后才改为这样配置 --> <http security="none" pattern="/index.do" /> <!-- 因为要使用自己的权限验证规则,所以这里要配置access-decision-manager-ref 实际上,我只是在accessDecisionManager中增加了一个投票器,其它的属性都比较简单,不多说了 --> <http auto-config='true' access-decision-manager-ref="accessDecisionManager" access-denied-page="/index.do"> <intercept-url pattern="/demo.do*" access="IS_AUTHENTICATED_REMEMBERED" /> <intercept-url pattern="/**/*.do*" access="HODLE" /> <logout logout-url="/logout.do" invalidate-session="true" logout-success-url="/logout.jsp" /> <form-login login-page="/index.do" default-target-url="/frame.do" always-use-default-target="true" authentication-failure-url="/index.do?login_error=1" /> <session-management> <concurrency-control max-sessions="1" /> </session-management> </http> <!-- Automatically receives AuthenticationEvent messages --> <beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener" /> <!-- 认证管理器,使用自定义的UserDetailsService,并对密码采用md5加密--> <authentication-manager> <authentication-provider user-service-ref="userService"> <password-encoder hash="md5" /> </authentication-provider> </authentication-manager> <beans:bean id="userService" class="com.piaoyi.common.security.UserService" /> <!-- 访问决策管理器,这里使用AffirmativeBased,并加入一个自定义的投票器DynamicRoleVoter --> <beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased"> <beans:property name="decisionVoters"> <beans:list> <beans:bean class="org.springframework.security.access.vote.RoleVoter" /> <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter" /> <beans:bean class="com.piaoyi.common.security.DynamicRoleVoter" /> </beans:list> </beans:property> </beans:bean> </beans:beans>
UserService.java
- public class UserService implements UserDetailsService{
- @Autowired
- private ISystemUserService userService;
- @Override
- public UserDetails loadUserByUsername(String username)
- throws UsernameNotFoundException {
- // TODO Auto-generated method stub
- SystemUser user = userService.findById(username);
- if (user == null)
- throw new UsernameNotFoundException("The user name " + username
- + " can not be found!");
- List<GrantedAuthority> resultAuths = new ArrayList<SystemAuthority>();
- //增加access中配置的权限,实际上这里就是让所有登陆用户都具备该权限,
- //而真正的资源权限验证留给AccessDecisionManager来决定
- resultAuths.add(new GrantedAuthorityImpl("HODLE"));
- //验证用户名和密码是否正确,以及是否权限正确
- return new User(username, user.getPassword().toLowerCase(), user.isStatus(), true,
- true, true, resultAuths);
- }
- }
public class UserService implements UserDetailsService{
@Autowired
private ISystemUserService userService;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// TODO Auto-generated method stub
SystemUser user = userService.findById(username);
if (user == null)
throw new UsernameNotFoundException("The user name " + username
+ " can not be found!");
List<GrantedAuthority> resultAuths = new ArrayList<SystemAuthority>();
//增加access中配置的权限,实际上这里就是让所有登陆用户都具备该权限,
//而真正的资源权限验证留给AccessDecisionManager来决定
resultAuths.add(new GrantedAuthorityImpl("HODLE"));
//验证用户名和密码是否正确,以及是否权限正确
return new User(username, user.getPassword().toLowerCase(), user.isStatus(), true,
true, true, resultAuths);
}
}
DynamicRoleVoter.java
- public class DynamicRoleVoter implements
- AccessDecisionVoter {
- @Autowired
- private ISystemUserService userService;
- private PathMatcher pathMatcher = new AntPathMatcher();
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.security.vote.AccessDecisionVoter#supports(java.lang
- * .Class)
- */
- @SuppressWarnings("unchecked")
- public boolean supports(Class clazz) {
- return true;
- }
- /*
- * (non-Javadoc)
- *
- * @seeorg.springframework.security.vote.AccessDecisionVoter#supports(org.
- * springframework.security.ConfigAttribute)
- */
- public boolean supports(ConfigAttribute attribute) {
- return true;
- }
- /*
- * (non-Javadoc)
- *
- * @seeorg.springframework.security.vote.AccessDecisionVoter#vote(org.
- * springframework.security.Authentication, java.lang.Object,
- * org.springframework.security.ConfigAttributeDefinition)
- */
- public int vote(Authentication authentication, Object object,
- java.util.Collection arg2) {
- int result = ACCESS_ABSTAIN;
- if (!(object instanceof FilterInvocation))
- return result;
- FilterInvocation invo = (FilterInvocation) object;
- String url = invo.getRequestUrl();//当前请求的URL
- Set<GrantedAuthority> authorities = null;
- String userId = authentication.getName();
- //获得当前用户的可访问资源,自定义的查询方法,之后和当前请求资源进行匹配,成功则放行,否则拦截
- authorities = loadUserAuthorities(userService.findById(userId));
- Map<String, Set<String>> urlAuths = authService.getUrlAuthorities();
- Set<String> keySet = urlAuths.keySet();
- for (String key : keySet) {
- boolean matched = pathMatcher.match(key, url);
- if (!matched)
- continue;
- Set<String> mappedAuths = urlAuths.get(key);
- if (contain(authorities, mappedAuths)) {
- result = ACCESS_GRANTED;
- break;
- }
- }
- return result;
- }
- protected boolean contain(Set<GrantedAuthority> authorities,
- Set<String> mappedAuths) {
- if (CollectionUtils.isEmpty(mappedAuths)
- || CollectionUtils.isEmpty(authorities))
- return false;
- for (GrantedAuthority item : authorities) {
- if (mappedAuths.contains(item.getAuthority()))
- return true;
- }
- return false;
- }
- }
public class DynamicRoleVoter implements
AccessDecisionVoter {
@Autowired
private ISystemUserService userService;
private PathMatcher pathMatcher = new AntPathMatcher();
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.vote.AccessDecisionVoter#supports(java.lang
* .Class)
*/
@SuppressWarnings("unchecked")
public boolean supports(Class clazz) {
return true;
}
/*
* (non-Javadoc)
*
* @seeorg.springframework.security.vote.AccessDecisionVoter#supports(org.
* springframework.security.ConfigAttribute)
*/
public boolean supports(ConfigAttribute attribute) {
return true;
}
/*
* (non-Javadoc)
*
* @seeorg.springframework.security.vote.AccessDecisionVoter#vote(org.
* springframework.security.Authentication, java.lang.Object,
* org.springframework.security.ConfigAttributeDefinition)
*/
public int vote(Authentication authentication, Object object,
java.util.Collection arg2) {
int result = ACCESS_ABSTAIN;
if (!(object instanceof FilterInvocation))
return result;
FilterInvocation invo = (FilterInvocation) object;
String url = invo.getRequestUrl();//当前请求的URL
Set<GrantedAuthority> authorities = null;
String userId = authentication.getName();
//获得当前用户的可访问资源,自定义的查询方法,之后和当前请求资源进行匹配,成功则放行,否则拦截
authorities = loadUserAuthorities(userService.findById(userId));
Map<String, Set<String>> urlAuths = authService.getUrlAuthorities();
Set<String> keySet = urlAuths.keySet();
for (String key : keySet) {
boolean matched = pathMatcher.match(key, url);
if (!matched)
continue;
Set<String> mappedAuths = urlAuths.get(key);
if (contain(authorities, mappedAuths)) {
result = ACCESS_GRANTED;
break;
}
}
return result;
}
protected boolean contain(Set<GrantedAuthority> authorities,
Set<String> mappedAuths) {
if (CollectionUtils.isEmpty(mappedAuths)
|| CollectionUtils.isEmpty(authorities))
return false;
for (GrantedAuthority item : authorities) {
if (mappedAuths.contains(item.getAuthority()))
return true;
}
return false;
}
}
OK。