• Spring Boot Security 角色授权示例


    在本教程中,我将指导您如何使用 Spring 安全性根据用户的角色对用户进行弹簧启动应用程序授权。凭据和角色动态存储在MySQL数据库中。具有休眠功能的弹簧数据JPA用于数据访问层,百里叶与弹簧安全性的集成用于视图层。我们将编写代码来保护现有的Spring Boot项目产品管理器,本教程中对此进行了介绍。因此,我建议您下载该项目,以便轻松遵循本教程。对于授权,我们将创建一些具有不同角色(权限)的用户,如下所示:

    Username

    Roles

    patrick

    USER

    alex

    CREATOR

    john

    EDITOR

    namhm

    CREATOR, EDITOR

    admin

    ADMIN

    用户角色允许用户查看所有产品;角色创建者是创建新产品的权限;编辑角色用于编辑产品;和角色 ADMIN 向用户授予所有权限。

    1. 设计和创建表

    对于使用存储在数据库中的凭据和权限进行基于角色的授权,我们必须创建以下 3 个表:users 表存储凭据,角色表存储权限(权限)。用户角色之间的实体关系是多对多的,因为一个用户可以有一个或多个角色,并且一个角色可以分配为一个或多个用户。这就是为什么我们需要中间表users_roles来实现这种多对多关联。您可以执行以下 MySQL 脚本来创建这些表:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    CREATE TABLE `users` (
      `user_id` int(11) NOT NULL AUTO_INCREMENT,
      `email` varchar(45) NOT NULL,
      `full_name` varchar(45) NOT NULL,
      `password` varchar(64) NOT NULL,
      `enabled` tinyint(4) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `email_UNIQUE` (`email`)
    );
     
    CREATE TABLE `roles` (
      `role_id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(45) NOT NULL,
      PRIMARY KEY (`id`)
    );
     
    CREATE TABLE `users_roles` (
      `user_id` int(11) NOT NULL,
      `role_id` int(11) NOT NULL,
      KEY `user_fk_idx` (`user_id`),
      KEY `role_fk_idx` (`role_id`),
      CONSTRAINT `role_fk` FOREIGN KEY (`role_id`) REFERENCES `roles` (`role_id`),
      CONSTRAINT `user_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`)
    );
     

    执行以下 INSERT 语句以将 4 个角色插入到角色表中:运行以下 SQL 语句以在 users 表中创建 5 个用户:请注意,密码以 BCrypt 格式编码,并且与用户名相同。并执行以下脚本,根据上表向用户分配权限:这就是数据库的设置。

    1
    2
    3
    4
    INSERT INTO `roles` (`name`) VALUES ('USER');
    INSERT INTO `roles` (`name`) VALUES ('CREATOR');
    INSERT INTO `roles` (`name`) VALUES ('EDITOR');
    INSERT INTO `roles` (`name`) VALUES ('ADMIN');

    1
    2
    3
    4
    5
    INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('patrick', '$2a$10$cTUErxQqYVyU2qmQGIktpup5chLEdhD2zpzNEyYqmxrHHJbSNDOG.', '1');
    INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('alex', '$2a$10$.tP2OH3dEG0zms7vek4ated5AiQ.EGkncii0OpCcGq4bckS9NOULu', '1');
    INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('john', '$2a$10$E2UPv7arXmp3q0LzVzCBNeb4B4AtbTAGjkefVDnSztOwE7Gix6kea', '1');
    INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('namhm', '$2a$10$GQT8bfLMaLYwlyUysnGwDu6HMB5G.tin5MKT/uduv2Nez0.DmhnOq', '1');
    INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('admin', '$2a$10$IqTJTjn39IU5.7sSCDQxzu3xug6z/LPU6IF0azE/8CkHCwYEnwBX.', '1');

    1
    2
    3
    4
    5
    6
    INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (1, 1); -- user patrick has role USER
    INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (2, 2); -- user alex has role CREATOR
    INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (3, 3); -- user john has role EDITOR
    INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (4, 2); -- user namhm has role CREATOR
    INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (4, 3); -- user namhm has role EDITOR
    INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (5, 4); -- user admin has role ADMIN

    2. 配置数据源属性

    要将弹簧启动与弹簧数据JPA和休眠一起使用,请在应用程序属性中配置数据库连接信息,如下所示:修改与您的MySQL数据库匹配的URL,用户名和密码。

    1
    2
    3
    4
    spring.jpa.hibernate.ddl-auto=none
    spring.datasource.url=jdbc:mysql://localhost:3306/sales
    spring.datasource.username=root
    spring.datasource.password=password

    3. 配置依赖关系

    确保 Maven 构建文件包含以下针对弹簧 Web、弹簧数据 JPA、弹簧安全性、百里叶、MySQL JDBC 驱动程序和用于弹簧安全性的百里叶附加内容的依赖项声明:请注意,默认情况下,如果类路径中存在弹簧安全库,则用户必须登录才能使用该应用程序。

    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>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-jpaartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-securityartifactId>
    dependency>    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <scope>runtimescope>
    dependency>
    <dependency>
        <groupId>org.thymeleaf.extrasgroupId>
        <artifactId>thymeleaf-extras-springsecurity5artifactId>
    dependency>

    4. 代码实体类

    接下来,我们需要创建两个实体类,它们与数据库中的用户角色表进行映射。第一个类是角色:第二个类是 User:在这里,您可以看到我们在 User 类中使用一角色来映射从用户角色的单向多对多关联,例如 user.roles。有关休眠多对多关系映射的详细信息,请参阅本教程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package net.codejava;
     
    import javax.persistence.*;
     
    @Entity
    @Table(name = "roles")
    public class Role {
        @Id
        @Column(name = "role_id")
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
         
        private String name;
        public Integer getId() {
            return id;
        }
         
        // remaining getters and setters   
    }

    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
    26
    27
    28
    29
    30
    31
    32
    33
    package net.codejava;
     
    import java.util.*;
     
    import javax.persistence.*;
     
    @Entity
    @Table(name = "users")
    public class User {
     
        @Id
        @Column(name = "user_id")
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
     
        private String username;
        private String password;
        private boolean enabled;
         
        @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        @JoinTable(
                name = "users_roles",
                joinColumns = @JoinColumn(name = "user_id"),
                inverseJoinColumns = @JoinColumn(name = "role_id")
                )
        private Set roles = new HashSet<>();
     
        public Long getId() {
            return id;
        }
     
        // remaining getters and setters are not shown for brevity
    }

    5. 代码用户存储库

    接下来,使用以下代码创建用户存储库接口:此接口是由 Spring 数据 JPA 定义的 Crud存储库的子类型,因此 Spring 将在运行时生成实现类。我们定义了由 JPA 查询注释的 getUserBy用户名() 方法,以供春季安全用于身份验证。如果您不熟悉春季数据JPA,请查看此快速入门指南

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package net.codejava;
     
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.CrudRepository;
    import org.springframework.data.repository.query.Param;
     
    public interface UserRepository extends CrudRepository {
     
        @Query("SELECT u FROM User u WHERE u.username = :username")
        public User getUserByUsername(@Param("username") String username);
    }

    6. 实施用户详细信息和用户详细信息服务

    Spring Security需要一个用户详细信息接口的实现来了解经过身份验证的用户信息,因此我们创建了MyUserDetails类,如下所示:您可以看到,该类包装了用户类的实例,并将几乎重写的方法委托给用户的方法。对于授权,请注意此方法:此方法返回一组角色(权限),供Spring安全在授权过程中使用。接下来,我们需要用以下代码对由Spring安全定义的用户详细信息服务接口的实现进行编码:如您所见,此类在loadUserByUsername()方法中使用用户存储库接口的实例,该实例将在对用户进行身份验证时由Spring安全调用。

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    package net.codejava;
     
    import java.util.*;
     
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
     
    public class MyUserDetails implements UserDetails {
     
        private User user;
         
        public MyUserDetails(User user) {
            this.user = user;
        }
     
        @Override
        public Collectionextends GrantedAuthority> getAuthorities() {
            Set roles = user.getRoles();
            List authorities = new ArrayList<>();
             
            for (Role role : roles) {
                authorities.add(new SimpleGrantedAuthority(role.getName()));
            }
             
            return authorities;
        }
     
        @Override
        public String getPassword() {
            return user.getPassword();
        }
     
        @Override
        public String getUsername() {
            return user.getUsername();
        }
     
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
     
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
     
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
     
        @Override
        public boolean isEnabled() {
            return user.isEnabled();
        }
     
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Override
    public Collectionextends GrantedAuthority> getAuthorities() {
        Set roles = user.getRoles();
        List authorities = new ArrayList<>();
         
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
         
        return authorities;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package net.codejava;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.userdetails.*;
     
    public class UserDetailsServiceImpl implements UserDetailsService {
     
        @Autowired
        private UserRepository userRepository;
         
        @Override
        public UserDetails loadUserByUsername(String username)
                throws UsernameNotFoundException {
            User user = userRepository.getUserByUsername(username);
             
            if (user == null) {
                throw new UsernameNotFoundException("Could not find user");
            }
             
            return new MyUserDetails(user);
        }
     
    }

    7. 配置弹簧安全认证和授权

    为了将所有部分连接在一起,我们使用以下代码编写了一个Spring Security配置类:需要前4种方法来配置使用Spring数据JPA和Hibernate的身份验证提供程序。在最后一种方法中,我们为身份验证和授权配置HTTP安全性。我们还配置了一个自定义URL,用于在用户没有权限的情况下显示拒绝访问错误。

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    package net.codejava;
     
    import org.springframework.context.annotation.*;
    import org.springframework.security.authentication.dao.*;
    import org.springframework.security.config.annotation.authentication.builders.*;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.*;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
     
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     
        @Bean
        public UserDetailsService userDetailsService() {
            return new UserDetailsServiceImpl();
        }
         
        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
         
        @Bean
        public DaoAuthenticationProvider authenticationProvider() {
            DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
            authProvider.setUserDetailsService(userDetailsService());
            authProvider.setPasswordEncoder(passwordEncoder());
             
            return authProvider;
        }
     
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(authenticationProvider());
        }
     
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/").hasAnyAuthority("USER""CREATOR""EDITOR""ADMIN")
                .antMatchers("/new").hasAnyAuthority("ADMIN""CREATOR")
                .antMatchers("/edit/**").hasAnyAuthority("ADMIN""EDITOR")
                .antMatchers("/delete/**").hasAuthority("ADMIN")
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll()
                .and()
                .logout().permitAll()
                .and()
                .exceptionHandling().accessDeniedPage("/403")
                ;
        }
    }

    8. 使用百里叶集成与弹簧安全实现授权

    若要将 Thymeleaf 与 Spring Security 结合使用视图,请确保像这样声明相关的 XML 命名空间:要显示已登录用户的用户名,请使用以下代码:若要显示当前用户的所有角色(权限/权限/权利),请使用以下代码:显示仅适用于经过身份验证的用户的部分, 使用以下代码:要显示“注销”按钮:由于只有具有“创建者”或“管理员”角色的用户才能创建新产品,因此编写以下代码以显示“创建新产品”链接,该链接仅对授权用户可见:具有“编辑”或“管理员”角色的用户可以看到编辑/更新产品的链接, 因此,以下代码:只有管理员用户才能看到删除产品的链接:这基本上就是如何使用Thymeleaf和Spring Security在视图层中授权用户。

    1
    2
    <html xmlns:th="http://www.thymeleaf.org"
        xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">

    1
    <span sec:authentication="name">Usernamespan>

    1
    <span sec:authentication="principal.authorities">Rolesspan>

    1
    2
    3
    4
    5
    <div sec:authorize="isAuthenticated()">
        Welcome <b><span sec:authentication="name">Usernamespan>b>
         
        <i><span sec:authentication="principal.authorities">Rolesspan>i>
    div>

    1
    2
    3
    <form th:action="@{/logout}" method="post">
        <input type="submit" value="Logout" />
    form>

    1
    2
    3
    <div sec:authorize="hasAnyAuthority('CREATOR', 'ADMIN')">
        <a href="/new">Create New Producta>
    div>

    1
    2
    3
    <div sec:authorize="hasAnyAuthority('ADMIN', 'EDITOR')">
        <a th:href="/@{'/edit/' + ${product.id}}">Edita>
    div>

    1
    2
    3
    <div sec:authorize="hasAuthority('ADMIN')">
        <a th:href="/@{'/delete/' + ${product.id}}">Deletea>
    div>

    结论:到目前为止,您已经学会了如何使用弹簧安全软件和Thymeleaf根据用户的角色为弹簧启动应用程序授权用户。你看,Spring框架使以最小的努力实现授权变得简单方便。作为参考,您可以下载下面附带的示例项目。

    附件:
    弹簧安全授权.zip[示例弹簧启动安全项目]87 千字节
  • 相关阅读:
    这样做框架结构图,让你的PPT更有创意!
    网络安全专家,这5本入门秘籍人手一套
    manim边学边做--Matrix
    【Leetcode刷题】复制带随机指针的链表&&有效的括号
    机械臂速成小指南(九):正运动学分析
    九、面向对象 之 继承
    COCO数据集(Common Objects in COntext)
    黑马Java热门面试题SpringBoot(七)
    【 背包九讲——完全背包问题】
    linux内核中听过就能记住的概念
  • 原文地址:https://blog.csdn.net/allway2/article/details/127541774