如何在 Spring Boot 中实现基于角色权限的系统

Posted

技术标签:

【中文标题】如何在 Spring Boot 中实现基于角色权限的系统【英文标题】:How to implement Role Permission based system in Spring Boot 【发布时间】:2016-11-09 19:14:30 【问题描述】:

我正在尝试使用 spring boot 和 spring security 来实现基于角色权限的系统。为此,我以http://www.baeldung.com/role-and-privilege-for-spring-security-registration 为例 ,但没能做到。

努力

SpringSecurity 配置

package com.insight;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter

        @Autowired
        private UserDetailsService userDetailsService;

        @Autowired
        private AuthenticationSuccessHandler myAuthenticationSuccessHandler;

        @Autowired
        private LogoutSuccessHandler myLogoutSuccessHandler;

        @Autowired
        private AuthenticationFailureHandler authenticationFailureHandler;

        @Override
        protected void configure(final AuthenticationManagerBuilder auth) throws Exception 
            auth.authenticationProvider(authProvider());
        

        @Override
        public void configure(final WebSecurity web) throws Exception 
            web.ignoring().antMatchers("/resources/**");
        

        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception 
            httpSecurity.authorizeRequests()
            .antMatchers("/js/**","/css/**","/images/**","/fonts/**").permitAll()
            .antMatchers("/user/signup/**","/about", "/","/user/login/").permitAll() // #4
            .anyRequest().authenticated() // 7
            .and()
            .formLogin().failureUrl("/user/login?error=true")
            .defaultSuccessUrl("/")
            .loginProcessingUrl("/user/validateLogin")
            .usernameParameter("email")
            .passwordParameter("password")
            .loginPage("/user/login")
            .permitAll()
            .and()
            .logout().logoutRequestMatcher(new AntPathRequestMatcher("/user/logout")).logoutSuccessUrl("/user/login")
            .permitAll();
        
        @Bean
        public DaoAuthenticationProvider authProvider() 
            final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
            authProvider.setUserDetailsService(userDetailsService);
            authProvider.setPasswordEncoder(encoder());
            return authProvider;
        

        @Bean
        public PasswordEncoder encoder() 
            return new BCryptPasswordEncoder(11);
        
    

用户模型

package com.insight.models;

import java.util.Collection;
import java.util.Date;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Email;

@Entity
@Table(name = "users")
public class User 


     // An autogenerated id (unique for each user in the db)
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;

      @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
      private Set<Address> addresses;

      @NotNull
      @OneToOne(cascade = CascadeType.ALL)
      @JoinColumn(name = "title")
      private Configuration title;

      @OneToOne(cascade=CascadeType.ALL)
        @JoinTable(name="user_roles",
            joinColumns = @JoinColumn(name="user_id", referencedColumnName="id"),
            inverseJoinColumns = @JoinColumn(name="role_id", referencedColumnName="id")
        )
        private Collection<Role> roles;

      @NotNull
      private String firstName;
      @NotNull
      private String lastName;

      @NotNull
      @Column(unique = true)
      @Email
      private String email;
      @NotNull
      private String password;

      private String profileImage;

      @Column(name = "created_at")
      private Date createdAt;
      @Column(name = "created_by")
      private String createdBy;

      @Column(name = "updated_at")
      private Date updatedAt;
      @Column(name = "updated_by")
      private String updatedBy;

      private String status;
      private String deleteFlag;
      private String confirmationCode;
      private String lastLoginAt;
      private boolean enabled;
      private boolean tokenExpired;

      public User() 
            super();
            this.enabled = false;
        

      @PrePersist
      void createdAt() 
        this.createdAt = this.updatedAt = new Date();
      

      @PreUpdate
      void updatedAt() 
        this.updatedAt = new Date();
      

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public Set<Address> getAddresses() 
        return addresses;
    

    public void setAddresses(Set<Address> addresses) 
        this.addresses = addresses;
    

    public Configuration getTitle() 
        return title;
    

    public void setTitle(Configuration title) 
        this.title = title;
    



    public Collection<Role> getRoles() 
        return roles;
    

    public void setRoles(Collection<Role> roles) 
        this.roles = roles;
    

    public String getFirstName() 
        return firstName;
    

    public void setFirstName(String firstName) 
        this.firstName = firstName;
    

    public String getLastName() 
        return lastName;
    

    public void setLastName(String lastName) 
        this.lastName = lastName;
    

    public String getEmail() 
        return email;
    

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

    public String getPassword() 
        return password;
    

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

    public String getProfileImage() 
        return profileImage;
    

    public void setProfileImage(String profileImage) 
        this.profileImage = profileImage;
    

    public Date getCreatedAt() 
        return createdAt;
    

    public void setCreatedAt(Date createdAt) 
        this.createdAt = createdAt;
    

    public String getCreatedBy() 
        return createdBy;
    

    public void setCreatedBy(String createdBy) 
        this.createdBy = createdBy;
    

    public Date getUpdatedAt() 
        return updatedAt;
    

    public void setUpdatedAt(Date updatedAt) 
        this.updatedAt = updatedAt;
    

    public String getUpdatedBy() 
        return updatedBy;
    

    public void setUpdatedBy(String updatedBy) 
        this.updatedBy = updatedBy;
    

    public String getStatus() 
        return status;
    

    public void setStatus(String status) 
        this.status = status;
    

    public String getDeleteFlag() 
        return deleteFlag;
    

    public void setDeleteFlag(String deleteFlag) 
        this.deleteFlag = deleteFlag;
    

    public String getConfirmationCode() 
        return confirmationCode;
    

    public void setConfirmationCode(String confirmationCode) 
        this.confirmationCode = confirmationCode;
    

    public String getLastLoginAt() 
        return lastLoginAt;
    

    public void setLastLoginAt(String lastLoginAt) 
        this.lastLoginAt = lastLoginAt;
    

    public boolean isEnabled() 
        return enabled;
    

    public void setEnabled(boolean enabled) 
        this.enabled = enabled;
    

    public boolean isTokenExpired() 
        return tokenExpired;
    

    public void setTokenExpired(boolean tokenExpired) 
        this.tokenExpired = tokenExpired;
    
    @Override
    public int hashCode() 
        final int prime = 31;
        int result = 1;
        result = (prime * result) + ((email == null) ? 0 : email.hashCode());
        return result;
    

    @Override
    public boolean equals(final Object obj) 
        if (this == obj) 
            return true;
        
        if (obj == null) 
            return false;
        
        if (getClass() != obj.getClass()) 
            return false;
        
        final User user = (User) obj;
        if (!email.equals(user.email)) 
            return false;
        
        return true;
    

    @Override
    public String toString() 
        final StringBuilder builder = new StringBuilder();
        builder.append("User [firstName=").append(firstName).append("]").append("[lastName=").append(lastName).append("]").append("[username").append(email).append("]");
        return builder.toString();
    


榜样

package com.insight.models;

import java.util.Collection;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name="roles")
public class Role 

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @ManyToMany(mappedBy = "roles")
    private Collection<User> userRoles;

    @ManyToMany
    @JoinTable(
        name = "roles_privileges", 
        joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"), 
        inverseJoinColumns = @JoinColumn(name = "permission_id", referencedColumnName = "id"))
    private Collection<Permission> permissions;

    public Role(String name) 
        this.name=name;
    

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    


    public String getName() 
        return name;
    

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

    public Collection<User> getUserRoles() 
        return userRoles;
    

    public void setUserRoles(Collection<User> userRoles) 
        this.userRoles = userRoles;
    

    public Collection<Permission> getPermissions() 
        return permissions;
    

    public void setPermissions(Collection<Permission> permissions) 
        this.permissions = permissions;
    

    @Override
    public int hashCode() 
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    

    @Override
    public boolean equals(final Object obj) 
        if (this == obj) 
            return true;
        
        if (obj == null) 
            return false;
        
        if (getClass() != obj.getClass()) 
            return false;
        
        final Role role = (Role) obj;
        if (!role.equals(role.name)) 
            return false;
        
        return true;
    

    @Override
    public String toString() 
        final StringBuilder builder = new StringBuilder();
        builder.append("Role [name=").append(name).append("]").append("[id=").append(id).append("]");
        return builder.toString();
    

权限模型

package com.insight.models;

import java.util.Collection;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;

public class Permission 

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @ManyToMany(mappedBy = "permissions")
    private Collection<Role> roles;

    public Permission(String name2) 
        name = name;
    

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getName() 
        return name;
    

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

    public Collection<Role> getRoles() 
        return roles;
    

    public void setRoles(Collection<Role> roles) 
        this.roles = roles;
    

    @Override
    public int hashCode() 
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    

    @Override
    public boolean equals(Object obj) 
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Permission other = (Permission) obj;
        if (name == null) 
            if (other.name != null)
                return false;
         else if (!name.equals(other.name))
            return false;
        return true;
    

    @Override
    public String toString() 
        final StringBuilder builder = new StringBuilder();
        builder.append("Permission [name=").append(name).append("]").append("[id=").append(id).append("]");
        return builder.toString();
    

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="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.insight</groupId>
    <artifactId>insight</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>insight</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
        </dependency>
         <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
         </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
            <version>1.3.6.RELEASE</version><!--$NO-MVN-MAN-VER$-->
         </dependency>
         <dependency>
            <groupId>org.passay</groupId>
            <artifactId>passay</artifactId>
            <version>1.0</version>
         </dependency>
         <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
         </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>    
</project>

CustomUserDetailsS​​ervice

package com.insight.services;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
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.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.insight.models.Permission;
import com.insight.models.Role;
import com.insight.models.User;
import com.insight.repositories.RoleRepository;
import com.insight.repositories.UserRepository;



@Service("userDetailsService")
@Transactional
public class CustomUserDetailsService implements UserDetailsService 

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private IUserService service;
    @Autowired
    private MessageSource messages;
    @Autowired
    private RoleRepository roleRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException 
        User user = userRepository.findByEmail(email);
        if (user == null) 
            return new org.springframework.security.core.userdetails.User(
              " ", " ", true, true, true, true, 
              getAuthorities(Arrays.asList(roleRepository.findByName("ROLE_USER"))));
        

        return new org.springframework.security.core.userdetails.User(
          user.getEmail(), user.getPassword(), user.isEnabled(), true, true, 
          true, getAuthorities(user.getRoles()));
    

    private Collection<? extends GrantedAuthority> getAuthorities(Collection<Role> roles) 
        return getGrantedAuthorities(getPrivileges(roles));
    

    private List<String> getPrivileges(Collection<Role> roles) 
        List<String> privileges = new ArrayList<String>();
        List<Permission> collection = new ArrayList<Permission>();
        for (Role role : roles) 
            collection.addAll(role.getPermissions());
        
        for (Permission item : collection) 
            privileges.add(item.getName());
        
        return privileges;
    

    private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) 
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        for (String privilege : privileges) 
            authorities.add(new SimpleGrantedAuthority(privilege));
        
        return authorities;
    

那里也有一些其他的课程,但由于篇幅较大,我没有发布,如果有人需要详细信息,我会发布。

当我尝试运行时,我在控制台上收到以下错误:

org.springframework.beans.factory.BeanCreationException:创建名为“org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration”的bean时出错:注入自动装配的依赖项失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:无法自动装配方法:public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.setFilterChainProxySecurityConfigurer(org.springframework.security.config.annotation.ObjectPostProcessor, java.util.List) 抛出 java.lang.Exception;嵌套异常是 org.springframework.beans.factory.BeanExpressionException:表达式解析失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:创建名为“securityConfiguration”的 bean 时出错:注入自动装配的依赖项失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:无法自动装配字段:私有 org.springframework.security.core.userdetails.UserDetailsS​​ervice com.insight.SecurityConfiguration.userDetailsS​​ervice;嵌套异常是 org.springframework.beans.factory.BeanCreationException:创建名为“userDetailsS​​ervice”的 bean 时出错:注入自动装配的依赖项失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:无法自动装配字段:私有 com.insight.repositories.UserRepository com.insight.services.CustomUserDetailsS​​ervice.userRepository;嵌套异常是 org.springframework.beans.factory.BeanCreationException:创建名称为“userRepository”的 bean 时出错:设置 bean 时无法创建类型为 [org.springframework.orm.jpa.SharedEntityManagerCreator] 的内部 bean '(inner bean)#778bd3a2'属性“实体管理器”;嵌套异常是 org.springframework.beans.factory.BeanCreationException:创建名称为“(内部 bean)#778bd3a2”的 bean 时出错:设置构造函数参数时无法解析对 bean 'entityManagerFactory' 的引用;嵌套异常是 org.springframework.beans.factory.BeanCreationException:在类路径资源 [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class] 中定义名称为“entityManagerFactory”的 bean 创建错误:调用 init 方法失败;嵌套异常是 javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:368) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在

请朋友们帮帮我。

【问题讨论】:

尝试用@EnableWebSecurity 为你的SecurityConfiguration.class 和用@Component 为你的CustomUserDetailsS​​ervice.class 注释。 您的数据库是否已设置并正在运行?这个错误“Error creation bean with name 'entityManagerFactory'”通常意味着数据库连接有问题。 @Mandy 你找到解决方案了吗? 【参考方案1】:

正如 Michal 指出的,这是一个数据库问题 -

在类路径资源 [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class] 中定义名称为“entityManagerFactory”的 bean 创建错误: 调用 init 方法失败;嵌套异常是 javax.persistence.PersistenceException: [PersistenceUnit:默认] 无法在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues 构建 Hibernate SessionFactory(AutowiredAnnotationBeanPostProcessor.java:334)

你的 MySQL 数据库可能没有运行,或者你可能没有在 spring 属性中提供数据源连接 [我看到你有一个 MySQL 依赖引用]

对于调试或测试用例,您可以尝试 H2 -

<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.200</version>
    <scope>test</scope>
</dependency>

【讨论】:

【参考方案2】:

@EnableWebSecurity注释你的SecurityConfiguration

【讨论】:

以上是关于如何在 Spring Boot 中实现基于角色权限的系统的主要内容,如果未能解决你的问题,请参考以下文章

如何在微服务架构中实现基于角色的安全性

如何在 Firebase 中实现基于角色的访问控制

Spring/Hibernate 应用程序中的角色/权限

AngularJS中实现权限控制 - 基于RBAC

Spring Boot + Shiro

如何在 Spring Boot 中实现刷新令牌