使用Spring Security、Spring Data Jpa实现的RBAC权限控制

正好这几天不是那么忙,所以就研究了一下Spring Security的使用,为了以后方便写篇帖子记录一下。

1.什么是Spring Security?

我想关于什么是Spring Security我都不需要在这里赘述,大家可以到网上百度一下,但是问了大家能快速的融入还是贴一下

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency
Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

2.Spring Security有什么?

我们可以使用Spring Security做基于表单的登录认证(form-login)、弹窗的登录认证(http-basic)、对受保护资源的访问控制、单点登录等等。

3.要怎么使用Spring Security?

对于SpringSecurity的登录认证和密码加密等现在都不做讨论,主要说一下对受保护资源的访问控制。

在Spring Security中最做URL权限鉴定最关键的一个类就是FilterSecurityInterceptor,FilterSecurityInterceptor中还需要2个关键的类AccessDecisionManager 和SecurityMetadataSource,这是2个接口类,我们主要的的是他们的实现类。

他们的作用分别是:

AccessDecisionManager做权限的验证

主要方法就是:

void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) ,

SecurityMetadataSource是注入元数据

主要方法是:

public Collection<ConfigAttribute> getAttributes(Object object)

public Collection<ConfigAttribute> getAllConfigAttributes()

开始设计了

主要的domain类

  1. User
  2. UserGroup
  3. Role
  4. Permission
  5. Menu
  6. Resource

这里重点贴一下User.java和Role.java

User.java

package org.ylez.web.domain;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.*;
import java.util.*;

/**
 * @FileName: User
 * @Author: 唐欢
 * @Date: 2016-05-09 22:14
 * @Tool: IntelliJ IDEA
 */
@Entity
@Table(name = "t_user")
public class User extends SuperClass implements UserDetails {

    @Column(unique = true, nullable = false, length = 30)
    private String username;

    @Column(nullable = false, length = 30)
    private String password;

    private int age;

    @Column(unique = true)
    private String email;

    @Column(unique = true, length = 11)
    private String phone;

    @ManyToMany(targetEntity = UserGroup.class, mappedBy = "users")
    private Set<UserGroup> userGroup = new HashSet();

    @ManyToMany(targetEntity = Role.class, fetch = FetchType.EAGER)
    @JoinTable(name = "m_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")})
    private Set<Role> roles = new HashSet<>();

    @Transient
    private Set<Role> authorities = new HashSet<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        // 包裹用户单独赋予的角色
        authorities.addAll(roles);

        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public Set<UserGroup> getUserGroup() {
        return userGroup;
    }

    public void setUserGroup(Set<UserGroup> userGroup) {
        this.userGroup = userGroup;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    public void setAuthorities(Set<Role> authorities) {
        this.authorities = authorities;
    }
}

Role.java

package org.ylez.web.domain;

import org.springframework.security.core.GrantedAuthority;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @FileName: Role
 * @Author: 唐欢
 * @Date: 2016-05-12 18:15
 * @Tool: IntelliJ IDEA
 */
@Entity
@Table(name = "t_role")
public class Role extends SuperClass implements GrantedAuthority {

    @Column(nullable = false, length = 30)
    private String name;

    @Column(nullable = false, length = 30)
    private String nickName;

    private String comment;

    @ManyToMany(targetEntity = Permission.class, fetch = FetchType.EAGER)
    @JoinTable(name = "m_role_permission", joinColumns = {@JoinColumn(name = "role_id")}, inverseJoinColumns = {@JoinColumn(name = "permission_id")})
    private Set<Permission> permissions = new HashSet<>();

    @ManyToMany(targetEntity = User.class, mappedBy = "roles")
    private Set<User> users = new HashSet<>();

    @ManyToMany(targetEntity = UserGroup.class, mappedBy = "roles")
    private Set<UserGroup> userGroups = new HashSet<>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Set<Permission> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<Permission> permissions) {
        this.permissions = permissions;
    }

    public Set<User> getUsers() {
        return users;
    }

    public void setUsers(Set<User> users) {
        this.users = users;
    }

    public Set<UserGroup> getUserGroups() {
        return userGroups;
    }

    public void setUserGroups(Set<UserGroup> userGroups) {
        this.userGroups = userGroups;
    }

    @Override
    public String getAuthority() {
        return name;
    }
}

创建UserDetailsService在登录的时候加载用户信息

package org.ylez.web.security.service;

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 org.ylez.web.domain.Role;
import org.ylez.web.domain.User;
import org.ylez.web.domain.UserGroup;
import org.ylez.web.service.UserService;

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

/**
 * @FileName: UserDetailsServiceImpl
 * @Author: 唐欢
 * @Date: 2016-05-10 09:39
 * @Tool: IntelliJ IDEA
 */
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.loadUserByUsername(username);
        if (user != null) {
            Set<UserGroup> userGroup = user.getUserGroup();

            for (UserGroup group : userGroup) {
                Set<Role> roles = group.getRoles();
                user.setAuthorities(roles);
            }
            return user;
        }else {
            throw new UsernameNotFoundException("找不到指定的用户信息!!!");
        }
    }
}

注意:

for (UserGroup group : userGroup) {
    Set<Role> roles = group.getRoles();
    user.setAuthorities(roles);
}

这里我讲用户所在的用户组所具有的角色也放置到用的authorities

接下类创建一个AuthorizationSecurityInterceptor.java的类源码如下:

package org.ylez.web.security.interceptor;

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.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;

/**
 * @FileName: AuthorizationSecurityInterceptor
 * @Author: 唐欢
 * @Date: 2016-05-12 17:24
 * @Tool: IntelliJ IDEA
 */
public class AuthorizationSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

    private static final String FILTER_APPLIED = "__Spring_Security_AuthorizationSecurityInterceptor_FilterApplied";

    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    private boolean observeOncePerRequest = true;

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

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

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

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        if ((fi.getRequest() != null)
                && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
                && observeOncePerRequest) {

            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        }
        else {
            if (fi.getRequest() != null) {
                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }

            InterceptorStatusToken token = super.beforeInvocation(fi);

            try {
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            }
            finally {
                super.finallyInvocation(token);
            }

            super.afterInvocation(token, null);
        }
    }

    public boolean isObserveOncePerRequest() {
        return observeOncePerRequest;
    }

    public void setObserveOncePerRequest(boolean observeOncePerRequest) {
        this.observeOncePerRequest = observeOncePerRequest;
    }
}

AuthorizationSecurityInterceptor的作用就是做URL的访问控制,需要注入AccessDecisionManager 和SecurityMetadataSource,可能有会奇怪为啥还要实现AuthorizationSecurityInterceptor,Spring Security不是为我们提供了FilterSecurityInterceptor吗?主要是SpringSecurity内部维护了一个FilterChain这个FilterChain是有一定的顺序的我们不能随便打乱,默认的FilterSecurityInterceptor也不太符合我们的需求,所以创建了AuthorizationSecurityInterceptor。

接下来创建AccessDecisionManagerImpl.java:

package org.ylez.web.security.accessdecisionmanager;

import org.springframework.context.MessageSource;
import org.springframework.context.support.MessageSourceAccessor;
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;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.util.StringUtils;
import org.ylez.web.domain.Role;

import java.util.Collection;

/**
 * @FileName: AccessDecisionManagerImpl
 * @Author: 唐欢
 * @Date: 2016-05-12 17:27
 * @Tool: IntelliJ IDEA
 */
public class AccessDecisionManagerImpl implements AccessDecisionManager {

    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

    /**
     * @param authentication 当前认证通过的用户的认证信息
     * @param object 当前用户访问的受保护资源,如URL
     * @param configAttributes 当前受保护资源需要的角色,权限
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

        for (GrantedAuthority authority : authorities) {
            // 角色名
            String roleName = authority.getAuthority();

            for (ConfigAttribute configAttribute : configAttributes) {
                if (configAttribute != null && !StringUtils.isEmpty(roleName)) {
                    if (configAttribute.getAttribute().equals(roleName)) {
                        return;
                    }
                }
            }
        }

        throw new AccessDeniedException(messages.getMessage(
                "AbstractAccessDecisionManager.accessDenied", "Access is denied"));
    }

    public void setMessageSource(MessageSource messageSource) {
        this.messages = new MessageSourceAccessor(messageSource);
    }

    /**
     * @param attribute 权限信息
     * @return
     */
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

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

AccessDecisionManagerImpl中的decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)方法注入了三个参数

Authentication authentication 是用户登录成功后的认证信息包含了当前用户所具有的角色信息。

Object object 是当前用户请求的受保护的资源。

Collection<ConfigAttribute> configAttributes 是当前用户请求的受保护资源应该具备的角色。

decide方法的逻辑就是将authentication中包含的角色和configAttributes中的角色进行比较,看authentication中是否有configAttributes种需要的角色,如果有就标识权限认证成功直接return方法就好如果没有就抛出异常throw
new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));。

接下来再创建FilterInvocationSecurityMetadataSourceImpl.java:

package org.ylez.web.security.metadatasource;

import org.springframework.beans.factory.annotation.Autowired;
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.ylez.web.domain.Permission;
import org.ylez.web.domain.Resource;
import org.ylez.web.domain.Role;
import org.ylez.web.service.ResourceService;

import java.util.*;

/**
 * @FileName: FilterInvocationSecurityMetadataSourceImpl
 * @Author: 唐欢
 * @Date: 2016-05-12 17:24
 * @Tool: IntelliJ IDEA
 */
public class FilterInvocationSecurityMetadataSourceImpl implements FilterInvocationSecurityMetadataSource {

    @Autowired
    private ResourceService resourceService;

    private Map<String, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();;
    /**
     * @param object 当前用户访问的受保护的资源
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

        System.err.println("-----------------------FilterInvocationSecurityMetadataSourceImpl.getAttributes-----------------------");

        String address = ((FilterInvocation) object).getRequestUrl();

        Resource resource = resourceService.findByAddress(address);
        if (resource != null) {
            String resourceName = resource.getName();

            Collection<ConfigAttribute> configAttributes = requestMap.get(resourceName);

            return configAttributes;
        }

        return new HashSet<>();
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {

        System.err.println("+++++++++++++++++++FilterInvocationSecurityMetadataSourceImpl.getAllConfigAttributes+++++++++++++++++++");

        Set<ConfigAttribute> allAttributes = new HashSet<>();

        List<Resource> resourceList = resourceService.findAll();

        for (Resource resource : resourceList) {

            Set<ConfigAttribute> itemAttributes = new HashSet<>();

            Set<Permission> permissions = resource.getPermissions();

            for (Permission permission : permissions) {

                Set<Role> roles = permission.getRoles();

                for (Role role : roles) {

                    ConfigAttribute ca = new SecurityConfig(role.getAuthority());

                    // 每一个请求资源对应的Role
                    itemAttributes.add(ca);

                    // 所有的Role对象
                    allAttributes.add(ca);
                }
            }
            String resourceName = resource.getName();

            requestMap.put(resourceName, itemAttributes);
        }
        return allAttributes;
    }

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

FilterInvocationSecurityMetadataSourceImpl中主要方法

public
Collection<ConfigAttribute> getAttributes(Object object)

public Collection<ConfigAttribute> getAllConfigAttributes()

getAllConfigAttributes() 是查询出所有的角色,该方法中我查询了所有的角色进行返回,该方法在程序启动的时候就会执行,所以做了一个requestMap

private Map<String, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();

来缓存请求资源和角色的对应关系。在getAttributes(Object
object)中我们就可以根据object直接从requestMap中取得。

getAttributes是根据访问的资源来查找对应所需角色,当请求过来时,我们可以根据参数object得到请求的URL去直接查询数据库,也可以在getAllConfigAttributes()查询所有做缓存我们从缓存中来查询,我这里做的还不是很好可以在优化一下。

现在就是在SecurityContext.xml中配置Spring Security了:

<?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:security="http://www.springframework.org/schema/security"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:settings.properties" />

    <security:http pattern="/admin/login.html" security="none"/>
    <security:http pattern="/react/**" security="none"/>
    <security:http pattern="/static/**" security="none"/>
    <security:http auto-config="true">
        <security:form-login
            login-page="/admin/login.html"
            login-processing-url="/admin/login.htm"
            username-parameter="username"
            password-parameter="password"
            default-target-url="/admin/index.html"
            always-use-default-target="true" />
        <security:http-basic/>
        <security:remember-me remember-me-parameter="rememberMe" key="ylez-unique-key" remember-me-cookie="ylez" />
        <security:logout logout-url="/logout.htm" invalidate-session="true" delete-cookies="ylez"/>
        <security:session-management invalid-session-url="/admin/login.html" session-fixation-protection="newSession">
            <security:concurrency-control max-sessions="2" expired-url="/admin/login.html" />
        </security:session-management>
        <security:port-mappings>
            <security:port-mapping http="80" https="443"/>
        </security:port-mappings>
        <security:csrf disabled="true"/>
        <security:custom-filter ref="authorizationSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>
    </security:http>

    <security:authentication-manager erase-credentials="false">
        <security:authentication-provider user-service-ref="userDetailsService">
            <security:password-encoder ref="passwordEncoder">
                <security:salt-source ref="saltSource"/>
            </security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- 密码加密工具,在用户注册的时候也是这个加密的工具. -->
    <bean id="passwordEncoder" class="org.ylez.web.security.password.PasswordEncoderImpl">
        <!-- 密码加密的时候的散列算法 -->
        <constructor-arg name="algorithm" value="${passwordEncoder.algorithm}" />
        <!-- 密码加密的时候的加密次数 -->
        <constructor-arg name="iterations" value="${passwordEncoder.iterations}" />
    </bean>

    <bean id="saltSource" class="org.ylez.web.security.salt.SaltSourceImpl" />

    <bean id="authorizationSecurityInterceptor" class="org.ylez.web.security.interceptor.AuthorizationSecurityInterceptor">
        <property name="accessDecisionManager" ref="accessDecisionManager"/>
        <property name="securityMetadataSource" ref="securityMetadataSource" />
    </bean>

    <bean id="accessDecisionManager" class="org.ylez.web.security.accessdecisionmanager.AccessDecisionManagerImpl" />

    <bean id="securityMetadataSource" class="org.ylez.web.security.metadatasource.FilterInvocationSecurityMetadataSourceImpl" />

    <bean id="userDetailsService" class="org.ylez.web.security.service.UserDetailsServiceImpl"/>
</beans>

Spring的配置文件

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="org.ylez.web.config" />

    <context:component-scan base-package="org.ylez.web.service" />

    <context:property-placeholder location="classpath:settings.properties" />

    <aop:aspectj-autoproxy expose-proxy="true"/>

    <import resource="securityContext.xml" />
</beans>

web.xml的配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 处理配置了懒加载的问题 -->
    <filter>
        <filter-name>openEntityManagerInViewFilter</filter-name>
        <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>openEntityManagerInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <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>/*</url-pattern>
    </filter-mapping>

    <!-- 配置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>

    <!-- 配置SpringMVC的Servlet -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:safeApp-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

Jpa的配置:

package org.ylez.web.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @FileName: JpaConfig
 * @Author: 唐欢
 * @Date: 2016-05-11 14:15
 * @Tool: IntelliJ IDEA
 */
@Configuration
@EnableJpaRepositories(basePackages = "org.ylez.web.repository")
@EnableTransactionManagement
public class JpaConfig {

    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/ylez?createDatabaseIfNotExist=true");
        dataSource.setUsername("root");
        dataSource.setPassword("admin");

        dataSource.setInitialSize(10);
        dataSource.setMinIdle(10);
        dataSource.setMaxActive(50);

        dataSource.setMaxWait(60000);
        dataSource.setTimeBetweenConnectErrorMillis(60000);

        dataSource.setMinEvictableIdleTimeMillis(300000);

        dataSource.setValidationQuery(" SELECT 'x' ");
        dataSource.setTestWhileIdle(true);
        dataSource.setTestOnBorrow(false);
        dataSource.setTestOnReturn(false);

        dataSource.setPoolPreparedStatements(false);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);

        return dataSource;
    }

    @Bean
    public EntityManagerFactory entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setGenerateDdl(true);
        adapter.setDatabase(Database.MYSQL);
        adapter.setShowSql(true);

        entityManagerFactory.setJpaVendorAdapter(adapter);
        entityManagerFactory.setDataSource(dataSource());
        entityManagerFactory.setPackagesToScan("org.ylez.web.domain");
        Map<String, Object> propsMap = new HashMap<>();
        propsMap.put("hibernate.format_sql", true);
        propsMap.put("hibernate.dialect", "org.hibernate.dialect.MySQL57InnoDBDialect");
        entityManagerFactory.setJpaPropertyMap(propsMap);
        entityManagerFactory.afterPropertiesSet();
        return entityManagerFactory.getObject();
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager(entityManagerFactory());
        return transactionManager;
    }
}

SQL代码:

/*
 Navicat Premium Data Transfer

 Source Server         : 127.0.0.1
 Source Server Type    : MySQL
 Source Server Version : 50711
 Source Host           : 127.0.0.1
 Source Database       : ylez

 Target Server Type    : MySQL
 Target Server Version : 50711
 File Encoding         : utf-8

 Date: 05/17/2016 00:15:22 AM
*/

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `m_permission_menu`
-- ----------------------------
DROP TABLE IF EXISTS `m_permission_menu`;
CREATE TABLE `m_permission_menu` (
  `permission_id` bigint(20) NOT NULL,
  `menu_id` bigint(20) NOT NULL,
  PRIMARY KEY (`permission_id`,`menu_id`),
  KEY `FKrck475a4xibvhvbipdsbml4jo` (`menu_id`),
  CONSTRAINT `FK892mp1voq26r0krjfnoqqfy8e` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`),
  CONSTRAINT `FKrck475a4xibvhvbipdsbml4jo` FOREIGN KEY (`menu_id`) REFERENCES `t_menu` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Table structure for `m_permission_resource`
-- ----------------------------
DROP TABLE IF EXISTS `m_permission_resource`;
CREATE TABLE `m_permission_resource` (
  `permission_id` bigint(20) NOT NULL,
  `resource_id` bigint(20) NOT NULL,
  PRIMARY KEY (`permission_id`,`resource_id`),
  KEY `FKqiqcr9msd3laua6pgk03gthth` (`resource_id`),
  CONSTRAINT `FK5wgud3b6ohn30iix2c4dhxgmm` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`),
  CONSTRAINT `FKqiqcr9msd3laua6pgk03gthth` FOREIGN KEY (`resource_id`) REFERENCES `t_resource` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `m_permission_resource`
-- ----------------------------
BEGIN;
INSERT INTO `m_permission_resource` VALUES ('1', '1'), ('2', '2');
COMMIT;

-- ----------------------------
--  Table structure for `m_role_permission`
-- ----------------------------
DROP TABLE IF EXISTS `m_role_permission`;
CREATE TABLE `m_role_permission` (
  `role_id` bigint(20) NOT NULL,
  `permission_id` bigint(20) NOT NULL,
  PRIMARY KEY (`role_id`,`permission_id`),
  KEY `FKjsv718s6pysaxl3hwdbh32v16` (`permission_id`),
  CONSTRAINT `FK71nkax6g3ndcd1q4iue3xfksb` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`),
  CONSTRAINT `FKjsv718s6pysaxl3hwdbh32v16` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `m_role_permission`
-- ----------------------------
BEGIN;
INSERT INTO `m_role_permission` VALUES ('1', '1'), ('1', '2'), ('2', '2');
COMMIT;

-- ----------------------------
--  Table structure for `m_user_role`
-- ----------------------------
DROP TABLE IF EXISTS `m_user_role`;
CREATE TABLE `m_user_role` (
  `user_id` bigint(20) NOT NULL,
  `role_id` bigint(20) NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `FK3kaylpusfxvtsulifg7u1o0cj` (`role_id`),
  CONSTRAINT `FK3kaylpusfxvtsulifg7u1o0cj` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`),
  CONSTRAINT `FK6520i3b07huaey0kr9fw49ln` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `m_user_role`
-- ----------------------------
BEGIN;
INSERT INTO `m_user_role` VALUES ('1', '1'), ('2', '2');
COMMIT;

-- ----------------------------
--  Table structure for `m_usergroup_role`
-- ----------------------------
DROP TABLE IF EXISTS `m_usergroup_role`;
CREATE TABLE `m_usergroup_role` (
  `usergroup_id` bigint(20) NOT NULL,
  `role_id` bigint(20) NOT NULL,
  PRIMARY KEY (`usergroup_id`,`role_id`),
  KEY `FKecy0fvxdsy58ku64uhsd1y1qd` (`role_id`),
  CONSTRAINT `FKecy0fvxdsy58ku64uhsd1y1qd` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`),
  CONSTRAINT `FKksc98i8kfc50g8bo4imvep8uk` FOREIGN KEY (`usergroup_id`) REFERENCES `t_usergroup` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `m_usergroup_role`
-- ----------------------------
BEGIN;
INSERT INTO `m_usergroup_role` VALUES ('1', '1'), ('1', '2');
COMMIT;

-- ----------------------------
--  Table structure for `m_usergroup_user`
-- ----------------------------
DROP TABLE IF EXISTS `m_usergroup_user`;
CREATE TABLE `m_usergroup_user` (
  `usergroup_id` bigint(20) NOT NULL,
  `user_id` bigint(20) NOT NULL,
  PRIMARY KEY (`usergroup_id`,`user_id`),
  KEY `FKl53jhmxcy4qy79chcy26wlw4a` (`user_id`),
  CONSTRAINT `FKl53jhmxcy4qy79chcy26wlw4a` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`),
  CONSTRAINT `FKml2qomhd7tkphcypjel80kbgj` FOREIGN KEY (`usergroup_id`) REFERENCES `t_usergroup` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `m_usergroup_user`
-- ----------------------------
BEGIN;
INSERT INTO `m_usergroup_user` VALUES ('1', '1');
COMMIT;

-- ----------------------------
--  Table structure for `t_menu`
-- ----------------------------
DROP TABLE IF EXISTS `t_menu`;
CREATE TABLE `t_menu` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `createTime` datetime(6) DEFAULT NULL,
  `enabled` bit(1) NOT NULL,
  `updateTime` datetime(6) DEFAULT NULL,
  `icon` varchar(30) DEFAULT NULL,
  `level` varchar(30) NOT NULL,
  `name` varchar(30) NOT NULL,
  `status` int(11) NOT NULL,
  `url` varchar(50) NOT NULL,
  `parent_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK4paxqyebl0scq6ur9osr0f56k` (`parent_id`),
  CONSTRAINT `FK4paxqyebl0scq6ur9osr0f56k` FOREIGN KEY (`parent_id`) REFERENCES `t_menu` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Table structure for `t_permission`
-- ----------------------------
DROP TABLE IF EXISTS `t_permission`;
CREATE TABLE `t_permission` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `createTime` datetime(6) DEFAULT NULL,
  `enabled` bit(1) NOT NULL,
  `updateTime` datetime(6) DEFAULT NULL,
  `comment` varchar(255) DEFAULT NULL,
  `name` varchar(30) NOT NULL,
  `nickName` varchar(30) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `t_permission`
-- ----------------------------
BEGIN;
INSERT INTO `t_permission` VALUES ('1', '2016-05-16 17:18:41.000000', b'1', '2016-05-16 17:18:47.000000', '访问管理后台', 'MANAGE_INDEX', '管理后台'), ('2', '2016-05-16 17:19:36.000000', b'1', '2016-05-16 17:19:42.000000', '网站前台', 'INDEX', '网站前台');
COMMIT;

-- ----------------------------
--  Table structure for `t_resource`
-- ----------------------------
DROP TABLE IF EXISTS `t_resource`;
CREATE TABLE `t_resource` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `createTime` datetime(6) DEFAULT NULL,
  `enabled` bit(1) NOT NULL,
  `updateTime` datetime(6) DEFAULT NULL,
  `address` varchar(30) NOT NULL,
  `name` varchar(30) NOT NULL,
  `type` varchar(30) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `t_resource`
-- ----------------------------
BEGIN;
INSERT INTO `t_resource` VALUES ('1', '2016-05-16 17:35:56.000000', b'1', '2016-05-16 17:36:03.000000', '/admin/index.html', 'admin_index', 'a'), ('2', '2016-05-16 17:37:20.000000', b'1', '2016-05-16 17:37:25.000000', '/index.html', 'index', 'b');
COMMIT;

-- ----------------------------
--  Table structure for `t_role`
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `createTime` datetime(6) DEFAULT NULL,
  `enabled` bit(1) NOT NULL,
  `updateTime` datetime(6) DEFAULT NULL,
  `comment` varchar(255) DEFAULT NULL,
  `name` varchar(30) NOT NULL,
  `nickName` varchar(30) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `t_role`
-- ----------------------------
BEGIN;
INSERT INTO `t_role` VALUES ('1', '2016-05-16 17:16:42.000000', b'1', '2016-05-16 17:16:48.000000', '管理员', 'ADMIN', '管理员'), ('2', '2016-05-16 17:17:17.000000', b'1', '2016-05-16 17:17:24.000000', '普通用户', 'USER', '会员');
COMMIT;

-- ----------------------------
--  Table structure for `t_user`
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `createTime` datetime(6) DEFAULT NULL,
  `enabled` bit(1) NOT NULL,
  `updateTime` datetime(6) DEFAULT NULL,
  `age` int(11) NOT NULL,
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(30) NOT NULL,
  `phone` varchar(11) DEFAULT NULL,
  `username` varchar(30) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_jhib4legehrm4yscx9t3lirqi` (`username`),
  UNIQUE KEY `UK_i6qjjoe560mee5ajdg7v1o6mi` (`email`),
  UNIQUE KEY `UK_m5bu5erj83eubjsa1nyms0t89` (`phone`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `t_user`
-- ----------------------------
BEGIN;
INSERT INTO `t_user` VALUES ('1', '2016-05-16 17:14:58.000000', b'1', '2016-05-16 17:15:04.000000', '23', '[email protected]', 'tanghuan', '18280206033', 'tanghuan'), ('2', '2016-05-16 17:15:43.000000', b'1', '2016-05-16 17:15:50.000000', '22', '[email protected]', 'xuran', '12345678987', 'xuran');
COMMIT;

-- ----------------------------
--  Table structure for `t_usergroup`
-- ----------------------------
DROP TABLE IF EXISTS `t_usergroup`;
CREATE TABLE `t_usergroup` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `createTime` datetime(6) DEFAULT NULL,
  `enabled` bit(1) NOT NULL,
  `updateTime` datetime(6) DEFAULT NULL,
  `comment` varchar(255) DEFAULT NULL,
  `groupNum` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_daw8uwu1enonbvfjg2oau9gdm` (`groupNum`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `t_usergroup`
-- ----------------------------
BEGIN;
INSERT INTO `t_usergroup` VALUES ('1', '2016-05-16 23:53:32.000000', b'1', '2016-05-16 23:53:40.000000', '管理员组', '123456789', 'MANAGER_GROUP');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

附项目的结构图:

演示效果图:

欢迎大家吐槽

时间: 2024-11-05 14:40:17

使用Spring Security、Spring Data Jpa实现的RBAC权限控制的相关文章

Spring Security(17)——基于方法的权限控制

目录 1.1     intercept-methods定义方法权限控制 1.2     使用pointcut定义方法权限控制 1.3     使用注解定义方法权限控制 1.3.1    JSR-250注解 1.3.2    @Secured注解 1.3.3    支持表达式的注解 1.4     方法权限控制的拦截器 1.4.1    MethodSecurityInterceptor 1.4.2    AspectJMethodSecurityInterceptor 之前介绍的都是基于URL

SpringMVC+Spring Data JPA+Shiro+EasyUI简单权限管理系统

SpringMVC+Spring Data JPA+Shiro+EasyUI简单权限管理系统 一直想做一个管理系统,希望它简洁,能做一个demo使用.以后在研究学习的时候,可以在此基础上增加代码.我觉得权限管理系统很值得做,因为涉及关系数据库模式的设计,能学到很多东西.万事开头难,先做个简单的,以后再慢慢完善的.任何事情关键是要做,不能停留在想. 前端 由于之前没有多少前端编程经验,所以做起前端比较吃力.之前前端使用Bootstrap,发现需要自己编写很多前端代码,虽然花费了很多时间,但是页面做

Spring Security: Spring Security简介

Spring Security简介 安全领域的两大核心是:Authentication和Authorization . ·Authentication是身份认证,把控一个系统的入口. ·Authorization是授权,用于进行系统中功能的访问控制. Spring Security 为J2EE项目提供了一个综合的解决方案,支持Authentication和Authorization. Spring Security对于Authentication提供了很宽泛的支持.集成了多种认证技术: HTTP

spring security +spring boot 自定义 403 页面

用的spring security  做的权限控制, 当  访问没有权限, 跳转 会跳到默认403 页面.不符合当前项目需求. 一下是解决方式: package com.ycmedia; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.au

【JavaEE】SSH+Spring Security+Spring oauth2环境方法及example

原文  http://www.cnblogs.com/smarterplanet/p/4088479.html 现在加最后一样,就是oauth2,现在很多网站都有对应的移动版本,那么移动端访问服务端的服务怎么控制权限,我知道的主要是两种方法,第一是模拟浏览器,访问服务的时候会生成session,之后在移动端缓存cookie,每次网络请求都把cookie加上,还有一种就是通过oauth2,登录之后生成一个凭证,每次请求时携带凭证,当然oauth2更多的是为第三方应用提供访问自己服务的权限. oau

Spring Security+Spring MVC+Mybatis

项目环境:JDK8+maven3.0+MySQL 项目结构: pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=&qu

springmvc+spring+mybatis+maven项目集成shiro进行用户权限控制

项目结构: 1.maven项目的pom中引入shiro所需的jar包依赖关系 <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>org.ap

springmvc+spring+mybatis+maven项目集成shiro进行用户权限控制【转】

项目结构: 1.maven项目的pom中引入shiro所需的jar包依赖关系 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <dependency>     <groupid>javax.servlet</groupid>     javax.servlet-api</artifactid>     <version>3.0.1</version>

Spring Boot + Shiro 实现前后端分离、权限控制

本文总结自实习中对项目的重构.原先项目采用Springboot+freemarker模版,开发过程中觉得前端逻辑写的实在恶心,后端Controller层还必须返回Freemarker模版的ModelAndView,逐渐有了前后端分离的想法,由于之前,没有接触过,主要参考的还是网上的一些博客教程等,初步完成了前后端分离,在此记录以备查阅. 一.前后端分离思想 前端从后端剥离,形成一个前端工程,前端只利用Json来和后端进行交互,后端不返回页面,只返回Json数据.前后端之间完全通过public A