[转]Spring Security 可动态授权RBAC权限模块实践
2015-08-31 15:37
441 查看
RBAC:基于角色的访问控制(Role-Based Access Control)
先在web.xml 中配置一个过滤器(必须在Struts的过滤器之前)[html] view plaincopy
<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>
然后就是编写Spring安全的配置文件applicationContext-security.xml并配置到Spring解析的路径下
Spring Security主要做两件事,一件是认证,一件是授权。
认证
当用户访问受保护的信息却没有登录获得认证时,框架会自动将请求跳转到登录页面
在http标签中的
[html] view plaincopy
<form-login login-page="/page/login.jsp" />
配置。且该登录页面必须是不被拦截的。故要配置上
[html] view plaincopy
<intercept-url pattern="/page/login.jsp" filters="none" />
Web项目的认证如果在HTTP标签中配置了auto-config="true",框架就会自动的配置多8?个拦截器。
默认表单登录认证的是FORM_LOGIN_FILTER拦截器,我们可以直接写自定义的UserDetailsService,在这个类中实现方法
UserDetails loadUserByUsername(String username),从数据库获取用户信息,以及其拥有的角色。
[java] view plaincopy
@Service("myUserDetailsService")
public class MyUserDetailsServiceImpl extends BaseService implements UserDetailsService {
@Resource
private UserDao userDao;
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
User user = userDao.getUserByUsername(username);
List<Role> roles = user.getRoles();
Collection<GrantedAuthority> authorities = new LinkedList<GrantedAuthority>();
for (Role role : roles) {
authorities.add(new GrantedAuthorityImpl(role.getCode()));
}
UserDetails userDetails = new org.springframework.security.core.userdetails.User(username,user.getPassword(),Constants.STATE_VALID.equals(user.getState()),true,true,true,authorities);
return userDetails;
}
}
配置在
[html] view plaincopy
<authentication-manager alias="myAuthenticationManager">
<authentication-provider user-service-ref="myUserDetailsService">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
如果需要在登录的时候,在HTTP SESSION中配置做些操作的。就得配置自定义的FORM_LOGIN_FILTER了
在HTTP标签中加入
[html] view plaincopy
<custom-filter ref="loginFilter" before="FORM_LOGIN_FILTER" />
并配置
[html] view plaincopy
<!-- 访问控制验证器Authority -->
<beans:bean id="securityFilter"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="myAuthenticationManager" />
<beans:property name="accessDecisionManager"
ref="affirmativeBasedAccessDecisionManager" />
<beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>
</beans:bean>
MyUsernamePasswordAuthenticationFilter 类是这么写的
[java] view plaincopy
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
@Resource
private LoginService loginService;
private UserLoginFormBean userLoginFormBean = new UserLoginFormBean();
@Resource
private LogService logService;
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = obtainUsername(request);
String password = obtainPassword(request);
HttpSession session = request.getSession();
userLoginFormBean.setUsername(obtainUsername(request));
userLoginFormBean.setPassword(obtainPassword(request));
User user = loginService.login(userLoginFormBean);
session.setAttribute(Constants.SESSION_USER, user);
Log log = new Log(user,getIpAddr(request),"用户登录", null);
logService.add(log);
//UsernamePasswordAuthenticationToken实现 Authentication
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Place the last username attempted into HttpSession for views
// 允许子类设置详细属性
setDetails(request, authRequest);
// 运行UserDetailsService的loadUserByUsername 再次封装Authentication
return this.getAuthenticationManager().authenticate(authRequest);
}
}
getAuthenticationManager().authenticate(authRequest)是为了让UserDetailService提供Detailed的信息并认证
授权
在授权时,系统默认通过FILTER_SECURITY_INTERCEPTOR认证。
如需自定义授权拦截器,我们在HTTP中在默认授权拦截器前配置了自定义的拦截器
[html] view plaincopy
<custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" />
本平台采用基于请求URL地址的验证方式
securityFilter的配置如下
[html] view plaincopy
<!-- 访问控制验证器Authority -->
<beans:bean id="securityFilter"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="myAuthenticationManager" />
<beans:property name="accessDecisionManager"
ref="affirmativeBasedAccessDecisionManager" />
<beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>
</beans:bean>
采用默认的自定义的也是采用Spring默认的FilterSecurityInterceptor拦截器,accessDecisionManager也采用的是框架提供的affirmativeBasedAccessDecisionManager
采用投票者来判断是否授权。
[html] view plaincopy
<beans:bean id="affirmativeBasedAccessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="decisionVoters" ref="roleDecisionVoter" />
</beans:bean>
<beans:bean name="roleDecisionVoter"
class="org.springframework.security.access.vote.RoleVoter" />
<beans:bean id="mySecurityMetadataSource"
class="org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource">
<beans:constructor-arg
type="org.springframework.security.web.util.UrlMatcher" ref="antUrlPathMatcher" />
<beans:constructor-arg type="java.util.LinkedHashMap"
ref="securityRequestMapFactoryBean" />
</beans:bean>
SecurityMetadataSource也是ss
web框架提供的DefaultFilterInvocationSecurityMetadataSource,只是初始化参数中,一个选择
antUrl匹配,还是正则匹配,另一个是提供自定义的通过securityRequestMapFactoryBean。在后者是一个
LinkedHashMap<RequestKey,
Collection<ConfigAttribute>>类型,就是每一个URL匹配模式,所需要角色的集合。
[java] view plaincopy
@Service("securityRequestMapFactoryBean")
public class SecurityRequestMapFactoryBean extends
LinkedHashMap<RequestKey, Collection<ConfigAttribute>> {
@Resource
private ModuleDao moduleDao;
@PostConstruct
public void loadSecurityInfos(){
List<Module> modules = moduleDao.getAll(new Module());
// List<Role> roles = roleDao.getAll(new Role());
for (Module module : modules) {
RequestKey requestKey = new RequestKey(module.getPageUrl());
Collection<ConfigAttribute> configAttributes = new LinkedList<ConfigAttribute>();
for (final Role role : module.getRoles()) {
configAttributes.add(new ConfigAttribute() {
public String getAttribute() {
return role.getCode();
}
});
}
this.put(requestKey, configAttributes);
}
}
}
PS: 最终的件applicationContext-security.xml配置文件
[html] view plaincopy
<pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?>
<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-3.0.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="/page/login.jsp" filters="none" />
<intercept-url pattern="/LoginAction*" filters="none" />
<intercept-url pattern="/common/**" filters="none" />
<intercept-url pattern="/css/**" filters="none" />
<intercept-url pattern="/common/**" filters="none" />
<intercept-url pattern="/images/**" filters="none" />
<intercept-url pattern="/js/**" filters="none" />
<form-login login-page="/page/login.jsp" />
<custom-filter ref="loginFilter" before="FORM_LOGIN_FILTER" />
<custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" />
</http>
<!-- 访问控制验证器Authority -->
<beans:bean id="securityFilter"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="myAuthenticationManager" />
<beans:property name="accessDecisionManager"
ref="affirmativeBasedAccessDecisionManager" />
<beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>
</beans:bean>
<!-- 登录验证器Authentication -->
<beans:bean id="loginFilter"
class="com.epro.crm.util.security.MyUsernamePasswordAuthenticationFilter">
<!-- 处理登录的action -->
<beans:property name="filterProcessesUrl" value="/SecurityCheck" />
<!-- 验证成功后的处理-->
<beans:property name="authenticationSuccessHandler"
ref="loginLogAuthenticationSuccessHandler" />
<!-- 验证失败后的处理-->
<beans:property name="authenticationFailureHandler"
ref="simpleUrlAuthenticationFailureHandler" />
<beans:property name="authenticationManager" ref="myAuthenticationManager" />
<!-- 注入DAO为了查询相应的用户 -->
<beans:property name="loginService" ref="loginService" />
<beans:property name="logService" ref="logService" />
</beans:bean>
<authentication-manager alias="myAuthenticationManager">
<authentication-provider user-service-ref="myUserDetailsService">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
<beans:bean id="mySecurityMetadataSource"
class="org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource">
<beans:constructor-arg
type="org.springframework.security.web.util.UrlMatcher" ref="antUrlPathMatcher" />
<beans:constructor-arg type="java.util.LinkedHashMap"
ref="securityRequestMapFactoryBean" />
</beans:bean>
<beans:bean id="antUrlPathMatcher"
class="org.springframework.security.web.util.AntUrlPathMatcher" />
<beans:bean id="affirmativeBasedAccessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="decisionVoters" ref="roleDecisionVoter" />
</beans:bean>
<beans:bean name="roleDecisionVoter"
class="org.springframework.security.access.vote.RoleVoter" />
<beans:bean id="loginLogAuthenticationSuccessHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/page/main.jsp"></beans:property>
</beans:bean>
<beans:bean id="simpleUrlAuthenticationFailureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<!--
可以配置相应的跳转方式。属性forwardToDestination为true采用forward false为sendRedirect
-->
<beans:property name="defaultFailureUrl" value="/page/login.jsp"></beans:property>
</beans:bean>
<!-- 未登录的切入点 -->
<beans:bean id="authenticationProcessingFilterEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/page/login.jsp"></beans:property>
</beans:bean>
</beans:beans></pre><br>
------------------------------------------------------ 分割线 -----------------------------------------------------------------<br>
<br>
后记:
<pre></pre>
由于权限配置信息,是由初始化mySecurityMetadataSource时,就由mySecurityMetadataSource读取提供的权限信息,并缓存与该类的私有成员变量中,所以重新加载时就需要重新新建一个对象
<pre></pre>
<pre name="code" class="java">public void loadSecurityInfos(){
this.clear();
List<Module> modules = moduleDao.getAll(new Module());
Collections.sort(modules);
for (Module module : modules) {
RequestKey requestKey = new RequestKey(module.getPageUrl());
Collection<ConfigAttribute> configAttributes = new LinkedList<ConfigAttribute>();
moduleDao.refresh(module);
List<Role> roles = module.getRoles();
if(roles != null){
for (final Role role : roles) {
configAttributes.add(new ConfigAttribute() {
public String getAttribute() {
return role.getCode();
}
});
}
}
this.put(requestKey, configAttributes);
log.info(module.getName()+ "模块 URL模式:" + requestKey + " 授权角色:"+ roles);
}
}</pre><br>
<br>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<div style="padding-top:20px">
<p style="font-size:12px;">版权声明:本文为博主原创文章,未经博主允许不得转载。</p>
</div>
相关文章推荐
- spring监听器ContextLoaderListener的疑问
- Java IO体系中常用的流分类
- 如何使用Struts 2防止表单重复提交?
- 用Eclipse开发JSF时,是标签出现自动提醒
- java集合中的Iterable接口的简介与运用
- java静态代理和动态代理(JDK&cglib)
- 重温java基础
- Spring之bean
- spring-InitializingBean init-method postConstruct
- openJDK开源项目列表
- Struts2中的两种输入校验方法
- 华为上机测试题(协议数据解析-java)
- 怎样从SpringMVC返回json数据
- java基础回顾
- Java中 NIO与IO的区别
- struts有关action配置的通配符配置问题
- JDK 1.7源码阅读笔记(一)String,StringBuilder,StringBuffer
- springmvc配置
- Eclipse导出Android应用程序以及在真机和仿真机的安装
- springMVC下无法加载静态资源文件(js、css、image)