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


      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>

      

  • 相关阅读:
    5的阶乘以及任意输入一个数的阶乘
    继入门程序后的第一篇函数调用的小程序 比较两数大小
    计算机网络01-计算机网络与因特网
    2021春招冲刺-1227 数组去重 | 响应式布局 | 媒体查询 |浏览器帧
    2021春招冲刺-1225 TCP与UDP | 单例模式 | 回流与重绘
    2021春招冲刺-1223 进程线程的通信 | 字符串是否有效 | 数组转换与展平
    2021春招冲刺-1221 进程与线程的区别 | 进程的切换 | 单链表是否相交 | 元素水平/垂直居中的方式
    左边固定,右边自适应解决方案
    mock 模拟数据在框架中的简单使用
    一个页面从输入url到加载到内容,这个过程经历了什么
  • 原文地址:https://www.cnblogs.com/luxh/p/3074520.html
Copyright © 2020-2023  润新知