先发几张图片看下大致的样子,美工不专业,有点渣,大家找自己想了解的就好。
实现功能:
1、当没有登录的时候访问数据库有的资源,直接跳转到登录页面
2、用户登录后,在url栏直接访问没有权限的资源,跳转到403页面。
3、控制用户重复登录和次数
4、用户和角色都允许分配权限
先看下web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<session-config>
<session-timeout>100</session-timeout>
</session-config>
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
<!-- 编码统一最好放最上面,最先加载,防止乱码-->
<!-- Spring编码转换过滤器,将请求信息的编码统一转换为UTF-8,避免中文乱码 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<!-- 然后接着是SpringSecurity必须的filter 优先配置,让SpringSecurity先加载,防止SpringSecurity拦截失效-->
<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>
<!--配置文件加载 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
WEB-INF/classes/applicationContext.xml,
WEB-INF/classes/config.xml,
WEB-INF/spring-security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--默认所对应的配置文件是WEB-INF下的{servlet-name}-servlet.xml,这里便是:demoweb-servlet.xml-->
<servlet>
<servlet-name>demoweb</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>demoweb</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
spring-mvc的配置文件demoweb-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<context:component-scan base-package="com.demo.web.app" />
<!-- 修改@ResponseBody返回中文乱码问题 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:order="2"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
<!-- FreeMarker配置 -->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
p:templateLoaderPath="/WEB-INF/ftl"
p:defaultEncoding="UTF-8">
<property name="freemarkerSettings">
<props>
<prop key="classic_compatible">true</prop>
</props>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
p:order="1"
p:suffix=".ftl"
p:contentType="text/html; charset=utf-8" />
<!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<!-- 指定所上传文件的总大小不能超过200KB。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
<property name="maxUploadSize" value="200000"/>
</bean>
<!-- SpringMVC在超出上传文件限制时,会抛出org.springframework.web.multipart.MaxUploadSizeExceededException -->
<!-- 该异常是SpringMVC在检查上传的文件信息时抛出来的,而且此时还没有进入到Controller方法中 -->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 遇到MaxUploadSizeExceededException异常时,自动跳转到/WEB-INF/jsp/error_fileupload.jsp页面 -->
<prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">error_fileupload</prop>
</props>
</property>
</bean>
<mvc:view-controller path="/" view-name="/index" />
</beans>
以上这两文件了解spring-mvc的应该都没什么问题
最重要的spring-security.xml文件
<?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="false" entry-point-ref="authenticationProcessingFilterEntryPoint" access-denied-page="/403.jsp"><!-- 当访问被拒绝时,会转到403.jsp -->
<!--filters="none" 该路径下不用过滤 -->
<intercept-url pattern="/themes/**" filters="none" />
<intercept-url pattern="/loginhtml.html" filters="none" />
<!-- session没用户时访问资源跳转的默认指定登陆页面,出错后跳转页面,成功页面 -->
<!-- <form-login login-page="/login.jsp" authentication-failure-url="/loginhtml.html?error=true" default-target-url="/login/login.html" /> -->
<!-- 退出销毁session, 退出系统转向url ,响应退出系统的url, 默认:/j_spring_security_logout-->
<logout invalidate-session="true" logout-success-url="/loginhtml.html" logout-url="/j_spring_security_logout"/>
<!-- 默认为false,此值表示:用户第二次登录时,前一次的登录信息都被清空 。 true系统拒绝登陆,后面试登陆次数 -->
<session-management >
<concurrency-control error-if-maximum-exceeded="true" max-sessions="1"/>
</session-management>
<http-basic />
<!--position表示替换掉Spring Security原来默认的登陆验证Filter。-->
<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" />
<!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->
<custom-filter before="FILTER_SECURITY_INTERCEPTOR"
ref="myFilter" />
</http>
<beans:bean id="loginFilter" class="com.demo.web.app.security.filter.MyUsernamePasswordAuthenticationFilter">
<!-- 处理登录的action -->
<beans:property name="filterProcessesUrl" value="/j_spring_security_check"></beans:property>
<!-- 验证成功后的处理-->
<beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property>
<!-- 验证失败后的处理-->
<beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property>
<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
</beans:bean>
<!-- 未登录的切入点 -->
<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/loginhtml.html"></beans:property>
</beans:bean>
<beans:bean id="loginLogAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/member/index.html"></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="/loginhtml.html"></beans:property>
</beans:bean>
<!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
我们的所有控制将在这三个类中实现,解释详见具体配置 -->
<beans:bean id="myFilter" class="com.demo.web.app.security.filter.MySecurityFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean>
<!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-manager alias="authenticationManager">
<authentication-provider
user-service-ref="myUserDetailService">
</authentication-provider>
</authentication-manager>
<beans:bean id="myUserDetailService" class="com.demo.web.app.security.MyUserDetailServiceImpl" />
<!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
<beans:bean id="myAccessDecisionManagerBean"
class="com.demo.web.app.security.MyAccessDecisionManager">
</beans:bean>
<!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->
<beans:bean id="securityMetadataSource" class="com.demo.web.app.security.MySecurityMetadataSource" >
<beans:constructor-arg name="resourcesService" ref="resourcesService"></beans:constructor-arg>
</beans:bean>
<beans:bean id="resourcesService" class="com.demo.web.app.service.resources.ResourcesService"></beans:bean>
</beans:beans>
下面把spring-secuity关键的说一下
<!-- 未登录的切入点 -->
<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/loginhtml.html"></beans:property>
</beans:bean>
也就是没登录的时候访问资源跳转到登录页,注意:要在http标签上加
auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint" access-denied-page="/403.jsp"
登录:
<beans:bean id="loginFilter" class="com.demo.web.app.security.filter.MyUsernamePasswordAuthenticationFilter">
<!-- 处理登录的action -->
<beans:property name="filterProcessesUrl" value="/j_spring_security_check"></beans:property>
<!-- 验证成功后的处理-->
<beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property>
<!-- 验证失败后的处理-->
<beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property>
<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
</beans:bean>
login.jsp的action为/j_spring_security_check,重新写了一个MyUsernamePasswordAuthenticationFilter
package com.demo.web.app.security.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import app.pojo.TUser;
import com.demo.web.app.service.user.UserService;
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
private Log log = LogFactory.getLog(MyUsernamePasswordAuthenticationFilter.class);
public static final String VALIDATE_CODE = "validateCode";
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
@Autowired
private UserService userService;
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
log.info("登录ing。。。。。。。。。。。");
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
TUser user = null;
try {
user = userService.getUserByUsername(username.trim());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(user==null){
throw new AuthenticationServiceException("用户名错误!");
}else{
if(!user.getPassword().equals(password)){
throw new AuthenticationServiceException("密码错误!");
}
}
//UsernamePasswordAuthenticationToken实现 Authentication
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// 允许子类设置详细属性
setDetails(request, authRequest);
// 运行UserDetailsService的loadUserByUsername 再次封装Authentication
return super.attemptAuthentication(request, response);
}
@Override
protected String obtainPassword(HttpServletRequest request) {
Object obj = request.getParameter(PASSWORD);
return null == obj ? "" : obj.toString();
}
@Override
protected String obtainUsername(HttpServletRequest request) {
Object obj = request.getParameter(USERNAME);
return null == obj ? "" : obj.toString();
}
}
这里主要的就是异常的抛出,可以在login.jsp通过${SPRING_SECURITY_LAST_EXCEPTION.message}取得,具体就不谈了。
权限控制的Filter,3个属性
<beans:bean id="myFilter" class="com.demo.web.app.security.filter.MySecurityFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean>
MySecurityFilter.java
package com.demo.web.app.security.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter{
private Log log = LogFactory.getLog(MySecurityFilter.class);
//与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应,
//其他的两个组件,已经在AbstractSecurityInterceptor定义
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Override
public Class<? extends Object> getSecureObjectClass() {
// TODO Auto-generated method stub
//下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
// TODO Auto-generated method stub
return this.securityMetadataSource;
}
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
private void invoke(FilterInvocation fi) throws IOException, ServletException {
// object为FilterInvocation对象
//super.beforeInvocation(fi);源码
//1.获取请求资源的权限
//执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);
//2.是否拥有权限
//this.accessDecisionManager.decide(authenticated, object, attributes);
log.info("------------MyFilterSecurityInterceptor.doFilter()-----------开始拦截器了....");
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
// Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);
// this.accessDecisionManager.decide(authenticated, object, attributes);
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} catch (Exception e) {
e.printStackTrace();
}finally {
super.afterInvocation(token, null);
}
log.info("------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....");
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
}
MyUserDetailServiceImpl.java
package com.demo.web.app.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import app.pojo.Application;
import app.pojo.Button;
import app.pojo.Menu;
import app.pojo.Privilege;
import app.pojo.TUser;
import com.demo.web.app.common.util.CommonUtil;
import com.demo.web.app.security.user.UserSession;
import com.demo.web.app.service.resources.ResourcesService;
import com.demo.web.app.service.user.UserService;
public class MyUserDetailServiceImpl implements UserDetailsService{
private Log log = LogFactory.getLog(MyUserDetailServiceImpl.class);
@Autowired
private UserService userService;
@Autowired
private ResourcesService resourcesService;
public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException, DataAccessException {
log.info("获取用户信息保存到全局缓存securityContextHolder,usernmae="+username);
TUser user = null;
user =userService.getUserByUsername(username);
if(user==null){
throw new UsernameNotFoundException(username);
}
Collection<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(user);
boolean enables = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
UserSession userSession = new UserSession(user.getUsername(), user.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuths);
userSession.setId(user.getId());
return userSession;
}
//取得用户的权限
private List<GrantedAuthority> obtionGrantedAuthorities(TUser user) {
List<GrantedAuthority> authSet = new ArrayList<GrantedAuthority>();
// authSet.add(new GrantedAuthorityImpl("ROLE_USER")); //用户基本权限
List<Privilege> ps = new ArrayList<Privilege>();
List<Privilege> psU = getPrivilegeByMasterId("user", user.getId());
ps.addAll(psU);
//先加载用户的权限,再去找相应角色的权限,都没有返回null
List<String> roleIds = getRoleIdsByUserId(user.getId());
for(String roleId : roleIds) {
List<Privilege> psR = getPrivilegeByMasterId("role",roleId);
ps.addAll(psR);
}
packAuthority(ps, authSet);
return authSet;
}
/**
* 加载用户拥有资源
* @param ps
* @param authSet
*/
public void packAuthority(List<Privilege> ps,List<GrantedAuthority> authSet){
for(Privilege p : ps){
authSet.add(new GrantedAuthorityImpl(p.getAccessCode()));
}
/* for(Privilege p : ps){
if(p.getPrivilegeAccess().equals("application")){
Application application = getApplication(p.getPrivilegeAccessValue());
log.info("加载application");
authSet.add(new GrantedAuthorityImpl(application.getApplicationCode())); //application
for(Privilege pmenu : ps){
if(pmenu.getPrivilegeAccess().equals("menu")){
Menu menu = getMenu(pmenu.getPrivilegeAccessValue());
log.info("加载menu");
if(menu.getApplicationId().equals(application.getApplicationId())){
authSet.add(new GrantedAuthorityImpl(menu.getMenuCode())); //menu
for(Privilege pbtn : ps){
if(pbtn.getPrivilegeAccess().equals("button")){
Button btn = getButton(pbtn.getPrivilegeAccessValue());
log.info("加载button");
if(btn.getMenuId().equals(menu.getMenuId())){
authSet.add(new GrantedAuthorityImpl(btn.getBtnCode())); //button
}
}
}
}
}
}
}
} */
}
private Button getButton(String buttonId) {
Button button = resourcesService.getButtonById(buttonId);
return button;
}
private Menu getMenu(String menuId) {
Menu menu = resourcesService.getMenuById(menuId);
return menu;
}
private List<Privilege> getPrivilegeByMasterId(String master,String id) {
List<Privilege> list = resourcesService.getPrivilegeByMasterId(master, id);
return list;
}
private Application getApplication(String applicationId) {
Application application = resourcesService.getApplicationById(applicationId);
return application;
}
private List<String> getRoleIdsByUserId(String id) {
List<String> roleIds = resourcesService.getRoleIdsByUserId(id);
return roleIds;
}
@Override
public boolean equals(Object obj) {
System.out.println(obj);
return super.equals(obj);
}
@Override
public int hashCode() {
System.out.println(MyUserDetailServiceImpl.this.hashCode());
return super.hashCode();
}
}
MySecurityMetadataSource.java
package com.demo.web.app.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import com.demo.web.app.service.resources.ResourcesService;
import app.pojo.Application;
import app.pojo.BaseResources;
import app.pojo.Button;
import app.pojo.Menu;
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource{
private ResourcesService resourcesService;
private Log log = LogFactory.getLog(MySecurityMetadataSource.class);
public MySecurityMetadataSource() {
}
//spring调用
public MySecurityMetadataSource(ResourcesService resourcesService) {
this.resourcesService = resourcesService;
loadResourceDefine();
}
/* 保存资源和权限的对应关系 key-资源url value-权限 */
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
public Collection<ConfigAttribute> getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
//返回所请求资源所需要的权限
//返回null是不执行下面MyAccessDecisionManager中的decide方法 出现null的情况:访问的资源在数据库中没有定义
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
String requestUrl = ((FilterInvocation) object).getRequestUrl();
log.info("MySecurityMetadataSource:getAttributes()---------------请求地址为:"+requestUrl);
if(resourceMap==null){
loadResourceDefine();
}
Iterator<String> it = resourceMap.keySet().iterator();
while(it.hasNext()){
String _url = it.next();
if(requestUrl.indexOf("?")!=-1){
requestUrl = requestUrl.substring(0, requestUrl.indexOf("?"));
}
if(_url.equals(requestUrl))
return resourceMap.get(_url);
}
log.info("请求的资源:"+requestUrl+" 在数据库中没有定义!");
return null;
// return resourceMap.get(requestUrl);
}
//加载数据库资源,封装成Map<String, Collection<ConfigAttribute>>
private void loadResourceDefine(){
log.info("MySecurityMetadataSource.loadResourcesDefine()--------------开始加载资源列表数据--------");
if(resourceMap == null) {
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
List<BaseResources> baseResources = resourcesService.getBaseResources();
List<Application> applications = resourcesService.getApplication();
List<Menu> menus = resourcesService.getMenu();
List<Button> buttons = resourcesService.getButton();
for(BaseResources base : baseResources){
Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
if(base.getBaseResourcesUrl()!=null && !base.getBaseResourcesUrl().equals("")){
ConfigAttribute configAttribute = new SecurityConfig(base.getBaseResourcesCode());
configAttributes.add(configAttribute);
resourceMap.put(base.getBaseResourcesUrl(), configAttributes);
}
}
for(Application application : applications){
Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
if(application.getApplicationUrl()!=null && !application.getApplicationUrl().equals("")){
ConfigAttribute configAttribute = new SecurityConfig(application.getApplicationCode());
configAttributes.add(configAttribute);
resourceMap.put(application.getApplicationUrl(), configAttributes);
}
}
for (Menu menu : menus) {
Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
//以menuCode封装为Spring的security Object
if(menu.getMenuUrl()!=null && !menu.getMenuUrl().equals("")){
ConfigAttribute configAttribute = new SecurityConfig(menu.getMenuCode());
configAttributes.add(configAttribute);
resourceMap.put(menu.getMenuUrl(), configAttributes);
}
}
for(Button button : buttons){
Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
if(button.getBtnUrl()!=null && !button.getBtnUrl().equals("")){
ConfigAttribute configAttribute = new SecurityConfig(button.getBtnCode());
configAttributes.add(configAttribute);
resourceMap.put(button.getBtnUrl(), configAttributes);
}
}
}
}
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
// System.out.println("MySecurityMetadataSource.supports()---------------------");
return true;
}
}
MyAccessDecisionManager.java
package com.demo.web.app.security;
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
public class MyAccessDecisionManager implements AccessDecisionManager{
private Log log = LogFactory.getLog(MyAccessDecisionManager.class);
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
InsufficientAuthenticationException {
log.info("MyAccessDescisionManager.decide()------------------验证用户是否具有一定的权限--------");
if(configAttributes==null){
return;
}
//所请求的资源拥有的权限(一个资源对多个权限)
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
while(iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
//访问所请求资源所需要的权限
String needPermission = configAttribute.getAttribute();
log.info("请求资源所需要的权限~~~~~~~~~~~~~~~~~~needPermission is " + needPermission);
//用户所拥有的权限authentication
for(GrantedAuthority ga : authentication.getAuthorities()) {
if(needPermission.equals(ga.getAuthority())) {
return;
}
}
}
//没有权限
throw new AccessDeniedException(" 没有权限访问! ");
}
/**
* 启动时候被AbstractSecurityInterceptor调用,决定AccessDecisionManager是否可以执行传递ConfigAttribute。
*/
public boolean supports(ConfigAttribute configAttribute) {
// TODO Auto-generated method stub
log.info("MyAccessDescisionManager.supports()------------角色名:"+configAttribute.getAttribute());
return true;
}
/**
* 被安全拦截器实现调用,包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型
*/
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
// System.out.println("MyAccessDescisionManager.supports()--------------------------------");
return true;
}
}
服务器启动时先执行MySecurityMetadataSource的loadResourceDefine()加载数据库资源,
登录时会调用authenticationManager的authentication-provider,
MyUserDetailServiceImpl的loadUserByUsername把用户信息和用户权限加载进来放到全局变量securityContextHolder中
本人实验这个好像也就在登录的时候执行下,这个session过期的问题还不清楚,感觉还是首次登录后计时,不是最后操作后计时。我也自己写了userSession,继承他的user
package com.demo.web.app.security.user;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import app.pojo.Application;
import app.pojo.Button;
import app.pojo.Menu;
/**
* spring security中存的 用户基本信息
* 即session中存储的用户信息
* @author Administrator
*
*/
public class UserSession extends User{
private String id;
private List<Application> applications;
private List<Menu> menus;
private List<Button> buttons;
public UserSession(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired,accountNonLocked, authorities);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Application> getApplications() {
return applications;
}
public void setApplications(List<Application> applications) {
this.applications = applications;
}
public List<Menu> getMenus() {
return menus;
}
public void setMenus(List<Menu> menus) {
this.menus = menus;
}
public List<Button> getButtons() {
return buttons;
}
public void setButtons(List<Button> buttons) {
this.buttons = buttons;
}
@Override
public boolean equals(Object rhs) {
// TODO Auto-generated method stub
return super.equals(rhs);
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return super.hashCode();
}
}
浏览器访问时MySecurityFilter拦截执行InterceptorStatusToken token = super.beforeInvocation(fi);
就会调用MySecurityMetadataSource 的getAttributes(Object object)方法,返回所请求资源所需要的权限,
再执行访问决策器myAccessDecisionManagerBean中的decide方法。我是url相同既有权限访问,基本上就这样了。
再看下数据表的结构:
权限关系表privilege
基础资源表baseResources
系统模块表application
button表
menu表
用户角色表user_role
用户表user
角色表role
由于application,button,menu表中没有定义一些其他的url资源,如:用户中心,获取菜单。。。而没有定义这些资源,当访问的时候就不会被拦截,还是可以直接访问。所以我定义了一个基础的资源表base_resources,当新建用户的时候就赋予用户这个权限,就管理了所有的url。
还有button,appliction,menu,base_resource表中的code是该资源需要的权限编码,我在privilege表也放入方便操作。
还有privilege表,主体可以为用户,角色。。。领域可以为模块,菜单,按钮,也就是who,what how 某某在某某领域拥有什么。
大致就这样了