北在北方

太白枝头看,花开不计年,杯中浮日月,楼外是青天。

导航

Spring Security3简单使用(权限配置在数据库中)

Posted on 2013-05-12 20:50  CN.programmer.Luxh  阅读(1614)  评论(3编辑  收藏  举报

  1、权限配置在数据库中,典型的五张表。

    1)t_user  用户表

    2)t_role  角色表

    3)t_user_role  用户-角色关联表 

    4)t_resource  资源表

    5)t_role_resource  角色-资源关联表

  2、建表语句

DROP TABLE IF EXISTS `t_resource`;
CREATE TABLE `t_resource` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`name`  varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`url`  varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)

DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`name`  varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)

DROP TABLE IF EXISTS `t_role_resource`;
CREATE TABLE `t_role_resource` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`role_id`  int(11) NULL DEFAULT NULL ,
`resource_id`  int(11) NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)

CREATE TABLE `t_user` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`account`  varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`password`  varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)

DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`user_id`  int(11) NULL DEFAULT NULL ,
`role_id`  int(11) NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)

  3、对应的领域实体

    1)用户

复制代码
package cn.luxh.app.domain;
/**
 * 用户
 * @author Luxh
 */
public class User {
    
    private Integer id;
    /**帐号*/
    private String account;
    /**密码*/
    private String password;
    
    
    @Override
    public int hashCode() {
        return account.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        User user = (User) obj;
        return this.account.equals(user.getAccount());
    }
    
    //getter setter
    //... }
复制代码

    2)角色

复制代码
package cn.luxh.app.domain;

/**
 * 角色
 * @author Luxh
 */
public class Role {
    
    private Integer id;
    /**角色名称*/
    private String name;
    
    //getter setter
    //...
}
复制代码

    3)用户-角色

复制代码
package cn.luxh.app.domain;
/**
 * 用户角色
 * @author Luxh
 */
public class UserRole {
    private Integer id;
    /**用户id*/
    private Integer userId;
    /**角色id*/
    private Integer roleId;
    
    //getter setter
    //...
}
复制代码

    4)资源

package cn.luxh.app.domain;

/**
 * 资源
 * @author Luxh
 */
public class Resource {
    
    private Integer id;
    /**资源名称*/
    private String name;
    /**访问地址*/
    private String url;
    
    //getter setter
}

    5)角色-资源

package cn.luxh.app.domain;
/**
 * 角色资源
 * @author Luxh
 */
public class RoleResource {
    private Integer id;
    /**角色id*/
    private Integer roleId;
    /**资源id*/
    private Integer resourceId;
    
    //getter setter
}

  4、配置文件

    在web.xml文件中加上如下内容:

复制代码
<!-- 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>  

 <!--  获取Spring Security session的生命周期-->  
    <listener>  
        <listener-class>  
         org.springframework.security.web.session.HttpSessionEventPublisher   
        </listener-class>  
    </listener>  
复制代码

    当然配置spring监听器的时候得把springsecurity的权限配置文件给加载进去:

复制代码
<!-- 配置Spring监听器 -->
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml,classpath:application-security.xml</param-value>
    </context-param>
复制代码

    权限配置文件内容如下:

<?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.2.xsd
                        http://www.springframework.org/schema/security 
                        http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    
    <http pattern="/login" security="none" />
    <http pattern="/resources/**" security="none" />
    

    <http auto-config="true" use-expressions="true" access-denied-page="/denied">
        <!-- default-target-url 指定了从登录页面登录后进行跳转的页面 always-use-default-target true表示登录成功后强制跳转 
            authentication-failure-url 表示验证失败后进入的页面 login-processing-url 设置验证登录验证地址,如果不设置,默认是j_spring_security_check 
            username-parameter,password-parameter 设置登录用户名和密码的请求name,默认:j_username,j_password 
            default-target-url="/user/home" -->
        <form-login login-page="/login"
            always-use-default-target="true"
            authentication-failure-url="/login?error=1"
            authentication-success-handler-ref="successHandler" />
        
        <logout   logout-success-url="/login" />

        <!-- error-if-maximum-exceeded 后登陆的账号会挤掉第一次登陆的账号 
            session-fixation-protection 
            防止伪造sessionid攻击. 用户登录成功后会销毁用户当前的session.   
                创建新的session,并把用户信息复制到新session中. -->

        <session-management invalid-session-url="/login?error=3"
            session-fixation-protection="none">
            <concurrency-control max-sessions="1"
                error-if-maximum-exceeded="true" expired-url="/login?error=2" /><!-- 阻止第二次登录 -->
        </session-management>
        <custom-filter ref="appInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>  
    </http>


    <authentication-manager alias="appAuthenticationManager">
        <authentication-provider user-service-ref="userDetailsService">
        </authentication-provider>
    </authentication-manager>
    
    <beans:bean id="appInterceptor" class="cn.luxh.app.security.AppSecurityInterceptor">
        <beans:property name="authenticationManager" ref="appAuthenticationManager"/>
        <beans:property name="accessDecisionManager" ref="appAccessDescisionManager"/>
        <beans:property name="securityMetadataSource" ref="appSecurityMetadataSource"/>
    </beans:bean>
    
    
    
    <beans:bean id="userDetailsService" class="cn.luxh.app.security.UserDetailsServiceImpl" />
    
    <beans:bean id="appSecurityMetadataSource" class="cn.luxh.app.security.AppSecurityMetadataSource"> 
        <beans:constructor-arg name="roleService" ref="roleService"></beans:constructor-arg>
        <beans:constructor-arg name="resourceService" ref="resourceService"></beans:constructor-arg>
    </beans:bean>
    
    <beans:bean id="appAccessDescisionManager" class="cn.luxh.app.security.AppAccessDescisionManager"/>
    
    <beans:bean id="roleService" class="cn.luxh.app.service.RoleServiceImpl"/>
    
    <beans:bean id="resourceService" class="cn.luxh.app.service.ResourceServiceImpl"/>
    
    <!-- 登录成功业务处理 -->
    <beans:bean id="successHandler"
        class="cn.luxh.app.security.LoginAuthenticationSuccessHandler">
        <beans:property name="url" value="/index"></beans:property> 
    </beans:bean>

</beans:beans>

  5、权限配置文件中的关键类

    1)UserDetailsServiceImpl

package cn.luxh.app.security;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import cn.luxh.app.domain.Role;
import cn.luxh.app.domain.User;
import cn.luxh.app.exception.UserException;
import cn.luxh.app.persistence.RoleMapper;
import cn.luxh.app.persistence.UserMapper;

public class UserDetailsServiceImpl implements UserDetailsService{
    
    private static Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
    
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private RoleMapper roleMapper;
    
    /**
     * @param account 登录帐号
     */
    public UserDetails loadUserByUsername(String account)
            throws UsernameNotFoundException {
        log.info("登录账号:"+account);
        org.springframework.security.core.userdetails.User userDetails = null;
        try {
            User user = userMapper.selectByAccount(account);
            if(user == null) {
                throw new UserException("帐号:"+account+" 不存在!");
            }
            Collection<GrantedAuthority> grantedAuthorities = getGrantedAuthorities(user);  
            
            boolean enables = true;  
            boolean accountNonExpired = true;  
            boolean credentialsNonExpired = true;  
            boolean accountNonLocked = true; 
            userDetails = new org.springframework.security.core.userdetails.User(user.getAccount(), user.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuthorities);  
        }catch(Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return userDetails;
    }
    
    /**
     * 根据用户获取该用户拥有的角色
     * @param user
     * @return
     */
    private Set<GrantedAuthority> getGrantedAuthorities(User user) {
        Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();  
        List<Role> roles = roleMapper.selectByUserId(user.getId()); 
        if(roles != null) {
            for(Role role : roles) {  
                grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
            }  
        }
        return grantedAuthorities;  
    }

}

    2)AppSecurityInterceptor

View Code
package cn.luxh.app.security;

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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;


/**
 * 自定义拦截器
 * @author Luxh
 */
public class AppSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{
    
    private static Logger log = LoggerFactory.getLogger(AppSecurityInterceptor.class);
    
    private FilterInvocationSecurityMetadataSource securityMetadataSource;
    
    
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain filterChain) throws IOException, ServletException {
        log.info("开始拦截......");
        FilterInvocation fi = new FilterInvocation(request, response, filterChain);
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {  
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  
        } catch (Exception e) {  
            e.printStackTrace();  
        }finally{  
            super.afterInvocation(token,null);  
        }  
    }

    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return securityMetadataSource;
    }
    
    
    @Override
    public void init(FilterConfig arg0) throws ServletException {
        
    }
    
    @Override
    public void destroy() {
        
    }

    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
        return securityMetadataSource;
    }

    public void setSecurityMetadataSource(
            FilterInvocationSecurityMetadataSource securityMetadataSource) {
        this.securityMetadataSource = securityMetadataSource;
    }
    
    
    

}

    3)AppAccessDescisionManager

View Code
package cn.luxh.app.security;

import java.util.Collection;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;

/**
 * 
 * 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 ;做最终的访问控制决定 .
 * @author Luxh
 *
 */
public class AppAccessDescisionManager implements AccessDecisionManager {
    
    private static Logger log = LoggerFactory.getLogger(AppAccessDescisionManager.class);
    
    /**
     * 认证用户是否具有权限访问该url地址 
     */
    @Override
    public void decide(Authentication authentication, Object obj,
            Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
            InsufficientAuthenticationException {
        log.info("AppAccessDescisionManager 验证用户是否具有访问资源的权限");
        
        if(configAttributes != null) {
            Iterator<ConfigAttribute> it = configAttributes.iterator(); 
            while(it.hasNext()) {
                //访问资源需要的权限
                String needRole = it.next().getAttribute();
                 //authentication.getAuthorities()  用户所有的权限  
                for(GrantedAuthority ga:authentication.getAuthorities()){  
                    if(needRole.equals(ga.getAuthority())){  
                        return;  
                    }  
                }  
            }
        }
        
        //没有权限  
        throw new AccessDeniedException("没有权限访问!");  
    }
    
    /** 
     * 启动时候被AbstractSecurityInterceptor调用,决定AccessDecisionManager是否可以执行传递ConfigAttribute。 
     */  
    @Override
    public boolean supports(ConfigAttribute arg0) {
        return true;
    }
    
    /** 
     * 被安全拦截器实现调用,包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型 
     */  
    @Override
    public boolean supports(Class<?> arg0) {
        return true;
    }

}

    4)AppSecurityMetadataSource

View Code
package cn.luxh.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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 org.springframework.util.AntPathMatcher;

import cn.luxh.app.domain.Resource;
import cn.luxh.app.domain.Role;
import cn.luxh.app.service.ResourceService;
import cn.luxh.app.service.RoleService;


/**
 * 从数据库中查询出资源和权限(角色),并将它们的关系对应起来
 * @author Luxh
 *
 */
public class AppSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    
    private static Logger log = LoggerFactory.getLogger(AppSecurityMetadataSource.class);
    
    private RoleService roleService;
    
    private ResourceService resourceService;
    
    public RoleService getRoleService() {
        return roleService;
    }


    public void setRoleService(RoleService roleService) {
        this.roleService = roleService;
    }


    public ResourceService getResourceService() {
        return resourceService;
    }


    public void setResourceService(ResourceService resourceService) {
        this.resourceService = resourceService;
    }



    private AntPathMatcher urlMatcher = new AntPathMatcher();
    
    
    /* 保存资源和权限的对应关系  key-资源url  value-权限 */  
    private Map<String,Collection<ConfigAttribute>> relationMap = null;
    
    
    public AppSecurityMetadataSource(RoleService roleService,ResourceService resourceService) {
        log.info("执行 AppSecurityMetadataSource 构造方法");
        this.roleService = roleService;
        this.resourceService = resourceService;
        loadResourceAndRoleRelation();
    }
    
    
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
    
    /**
     * 根据请求的url,获取访问该url所需的权限(角色)
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object obj)
            throws IllegalArgumentException {
         //获取请求的url地址  
        String requestUrl = ((FilterInvocation)obj).getRequestUrl();
        log.info("请求的 requestUrl :"+requestUrl);
        Iterator<String> it = relationMap.keySet().iterator();
        while(it.hasNext()) {
            String url = it.next();
            log.info("配置的 url :"+url);
            if(requestUrl.indexOf("?")!=-1) {
                requestUrl = requestUrl.substring(0, url.indexOf("?"));
            }
            log.info("hey man :"+url);
            if(urlMatcher.match(url, requestUrl)) {
                log.info("已匹配 :"+url);
                return relationMap.get(url);
            }
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> arg0) {
        return true;
    }
    
    

    /**
     * 加载资源和角色的对应关系
     */
    private void loadResourceAndRoleRelation() {
        relationMap = new HashMap<String,Collection<ConfigAttribute>>();
        //查出所有角色
        List<Role> roles = roleService.getAll();
        if(roles != null) {
            for(Role role : roles) {
                //查出某个角色可以访问的资源
                List<Resource> resources = resourceService.getByRoleId(role.getId());
                if(resources != null) {
                    for(Resource resource : resources) {
                        Collection<ConfigAttribute> configAttributes = null;  
                        ConfigAttribute configAttribute = new SecurityConfig(role.getName()); 
                        if(relationMap.containsKey(resource.getUrl())){  
                            configAttributes = relationMap.get(resource.getUrl());  
                            configAttributes.add(configAttribute);  
                        }else{  
                            configAttributes = new ArrayList<ConfigAttribute>() ;  
                            configAttributes.add(configAttribute);  
                            relationMap.put(resource.getUrl(), configAttributes);  
                         }  
                    }
                    
                }
                
            }
        }
    }

}

    5)LoginAuthenticationSuccessHandler

View Code
package cn.luxh.app.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

/**
 * 登录验证成功处理器
 * @author Luxh
 */

public class LoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
    
    private static Logger log = LoggerFactory.getLogger(LoginAuthenticationSuccessHandler.class);
    
    //登录验证成功后需要跳转的url
    private String url;
    
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication) throws IOException,
            ServletException {
        log.info("登录验证成功:"+request.getContextPath()+url);
        //response.sendRedirect(request.getContextPath()+url);
        request.getRequestDispatcher(url).forward(request, response);
    }
    
    public void setUrl(String url) {
        this.url = url;
    }

}

  6、其中资源表内容如下

              

  7、源码,含数据库文件和初始化值

        https://files.cnblogs.com/luxh/app4.rar

  8、在页面上的控制权限

    1)引入标签: <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

    2)使用标签:如下,审核操作时具有"ROLE_ADM"权限才可以看到。

<sec:authorize ifAnyGranted="ROLE_ADM">
           <li><a href="javascript:auditPage('${ctx}/task/auditPage?taskId=${task.taskId}')">审核</a></li>
</sec:authorize>