Spring Security认证和授权(二)

  • 1.1 资源准备
    • 1.2 资源授权的配置
      • 1.3 基于内存的多用户支持
        • 1.4 认证和授权
          • 1.4.1 数据库准备
          • 1.4.2 编码
        • 2.1 实现UserDetails
          • 2.2 数据库以及表准备
            • 2.3 实现UserDetailsService
              • 2.4 启动程序测试

                1. 默认数据库认证和授权

                1.1 资源准备

                首先准备三个不同权限的接口

                @GetMapping("/admin/test")
                @ResponseBody
                public String adminTest() {
                    return "admin test";
                }
                
                @GetMapping("/user/test")
                @ResponseBody
                public String userTest() {
                    return "user test";
                }
                
                @GetMapping("/web/test")
                @ResponseBody
                public String webTest() {
                    return "web test";
                }
                

                假设在/admin/test/下的内容是系统后台管理相关的 API,在/web/test下的内容是面向客户端公开访 问的API,在/user/test/下的内容是用户操作自身数据相关的API;显然,/admin/test必须拥有管理员权限才能进行操作,而/user/test必须在用户登录后才能进行操作。

                1.2 资源授权的配置

                /**
                 * @author: Java技术债务
                 * @Date: 2021/6/5 18:45
                 * Describe: SpringSecurity配置
                 */
                @Configuration
                @EnableWebSecurity
                @EnableGlobalMethodSecurity(prePostEnabled = true)
                @Slf4j
                public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                
                    @Override
                    public void configure(HttpSecurity http) throws Exception {
                        http.authorizeRequests()
                                .antMatchers("/admin/test/**").hasRole("admin")
                                .antMatchers("/user/test/**").hasRole("user")
                                .antMatchers("/web/test/**").permitAll()
                                .anyRequest().authenticated()
                                .and()
                                .formLogin();
                
                    }
                }
                

                antMatchers()是一个采用ANT模式的URL匹配器。ANT模式使用?匹配任意单个字符,使用* 匹配0或任意数量的字符,使用匹配0或者更多的目录。antMatchers("/admin/test/")相当于匹配 了/admin/test/下的所有API。此处我们指定当其必须为admin角色时才能访问,/user/test/与之同 理。/web/test/下的API会调用permitAll()公开其权限。

                重启服务测试

                在这里插入图片描述

                在这里插入图片描述

                页面显示403错误,表示该用户授权失败(401代表该用户认证失败)。也就是说,本次访问已经通过了认证环节,只是在授权的时候被驳回了。认证环节是没有问题的,因为Spring Security默认的用 户角色正是user。
                HTTP状态码(HTTP Status Code)是由RFC 2616定义的一种用来表示一个HTTP请求响应状态的 规范,由3位数字组成。通常用2XX表示本次操作成功,用4XX表示是客户端导致的失败,用5XX表示 是服务器引起的错误。

                访问/web/test/却成功了,因为/web/test/是允许所有角色访问。

                1.3 基于内存的多用户支持

                到目前为止,我们仍然只有一个可登录的用户,怎样引入多用户呢?非常简单,我们只需实现一
                个自定义的UserDetails Service即可。

                package com.demo.springSecurityFirstDemo.security;
                
                import lombok.extern.slf4j.Slf4j;
                import org.springframework.context.annotation.Bean;
                import org.springframework.context.annotation.Configuration;
                import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
                import org.springframework.security.config.annotation.web.builders.HttpSecurity;
                import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
                import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
                import org.springframework.security.core.userdetails.User;
                import org.springframework.security.core.userdetails.UserDetailsService;
                import org.springframework.security.provisioning.InMemoryUserDetailsManager;
                
                /**
                 * @author: Java技术债务
                 * @Date: 2021/6/5 18:45
                 * Describe: SpringSecurity配置
                 */
                @Configuration
                @EnableWebSecurity
                @EnableGlobalMethodSecurity(prePostEnabled = true)
                @Slf4j
                public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                
                    @Override
                    public void configure(HttpSecurity http) throws Exception {
                        http.authorizeRequests()
                                .antMatchers("/admin/test/**").hasRole("ADMIN")
                                .antMatchers("/user/test/**").hasRole("USER")
                                .antMatchers("/web/test/**").permitAll()
                                .anyRequest().authenticated()
                                .and()
                                .formLogin();
                
                    }
                
                    @Bean
                    public UserDetailsService userDetailsService() {
                        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
                        userDetailsManager.createUser(User.withUsername("user").password("1").roles("USER").build());
                        userDetailsManager.createUser(User.withUsername("admin").password("1").roles("USER", "ADMIN").build());
                
                        return userDetailsManager;
                    }
                }
                

                为其添加一个@bean注解,便可被 Spring Security 发现并使用。Spring Security支持各种来源的用户数据,包括内存、数据库、LDAP等。它们被抽象为一个UserDetailsService接口,任何实现了
                UserDetailsService 接口的对象都可以作为认证数据源。在这种设计模式下,Spring Security 显得尤为灵活。
                IMemory UserDetailsManager是 UserDetails Service接口中的一个实现类,它将用户数据源寄存在内存里,在一些不需要引入数据库这种重数据源的系统中很有帮助。

                1.4 认证和授权

                除了IMemoryUserDetailsManagsr, Spring Security还提供另一个UserDetailsService实现类:
                JdbcUserDetailsManager。

                JdbcUserDetailsManager帮助我们以JDBC的方式对接数据库和Spring Security,它设定了一个默认的数据库模型,只要遵从这个模型,在简便性上,JdbcUserDetailsManager甚至可以媲美InMemory UserDetailsManager。

                1.4.1 数据库准备

                数据库方面的不需要我多说什么了吧。

                唯一要说的就是创建的表

                在这里插入图片描述

                JdbcUserDetailsManager需要两个表,其中users表用来存放用户名、密码和是否可用三个信息, authorities表用来存放用户名及其权限的对应关系。将其复制到MySQL命令窗口执行时,会报错,因为该语句是用hsqldb创建的,而MySQL不支持 varchar_ignorecase这种类型。怎么办呢?很简单,将varchar_ignorecase改为MySQL支持的varchar即可。

                1.4.2 编码

                在这里插入图片描述

                JdbcUserDetailsManager与InMemoryUserDetailsManager在用法上没有太大区别,只是多了设置 DataSource 的环节。Spring Security 通过 DataSource 执行设定好的命令。

                例如,此处的createUser函数 实际上就是执行了下面的SQL语句。

                查看 JdbcUserDetailsManager 的源代码可以看到更多定义好的 SQL 语句,诸如deleteUserSql、 updateUserSql等,这些都是JdbcUserDetailsManager与数据库实际交互的形式。当然, JdbcUserDetailsManager 也允许我们在特殊情况下自定义这些 SQL语句,如有必要,调用对应的setXxxSql方法即可。

                在这里插入图片描述

                当使用Spring Security默认数据库模型应对各种用户系统时,难免灵活性欠佳。尤其是在对现有的 系统做Spring Security嵌入时,原本的用户数据已经固定,为了适配Spring Security而在数据库层面进行 修改显然得不偿失。强大而灵活的Spring Security对这方面进行了改进。

                2. 自定义数据库模型的认证与授权

                2.1 实现UserDetails

                之前使用了InMemoryUserDetailsManager 和 JdbcUserDetailsManager 两个UserDetailsService 实现类。生效方式也很简单,只需加入 Spring 的 IoC 容器,就会被 Spring Security自动发现并使用。自定义数据库结构实际上也仅需实现一个自定义的UserDetails Service。

                UserDetailsService仅定义了一个loadUserBy Username方法,用于获取一个UserDetails对象。UserDetails对象包含了一系列在验证时会用到的信息,包括用户名、密码、权限以及其他信息,Spring Security会根据这些信息判定验证是否成功。

                在这里插入图片描述

                也就是说,不管数据库结构如何变化,只要能构造一个UserDetails即可,下面就来实现这个过 程。

                2.2 数据库以及表准备

                SecurityUser.java

                package com.demo.springSecurityFirstDemo.security;
                
                import org.springframework.security.core.GrantedAuthority;
                import org.springframework.security.core.userdetails.UserDetails;
                
                import java.util.Collection;
                import java.util.List;
                
                /**
                 * @Author Java技术债务
                 * @Date 2022-03-26 19:12:56
                 * @Desc *
                 */
                public class SecurityUser implements UserDetails {
                    private Integer id;
                
                    private String username;
                
                    private String password;
                
                    private boolean enable;
                
                    private List<GrantedAuthority> authorityList;
                
                    public Integer getId() {
                        return id;
                    }
                
                    public void setId(Integer id) {
                        this.id = id;
                    }
                
                    public void setUsername(String username) {
                        this.username = username;
                    }
                
                    public void setPassword(String password) {
                        this.password = password;
                    }
                
                    public boolean isEnable() {
                        return enable;
                    }
                
                    public void setEnable(boolean enable) {
                        this.enable = enable;
                    }
                
                    public List<GrantedAuthority> getAuthorityList() {
                        return authorityList;
                    }
                
                    public void setAuthorityList(List<GrantedAuthority> authorityList) {
                        this.authorityList = authorityList;
                    }
                
                    @Override
                    public Collection<? extends GrantedAuthority> getAuthorities() {
                        return authorityList;
                    }
                
                    @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;
                    }
                
                    @Override
                    public boolean isEnabled() {
                        return enable;
                    }
                }
                

                实现UserDetails定义的几个方法:
                ◎ isAccountNonExpired、isAccountNonLocked 和 isCredentialsNonExpired 暂且用不到,统一返回 true,否则Spring Security会认为账号异常。
                ◎ isEnabled对应enable字段,将其代入即可。
                ◎ getAuthorities方法本身对应的是roles字段,但由于结构不一致,所以此处新建一个,并在后续 进行填充。

                2.3 实现UserDetailsService

                package com.demo.springSecurityFirstDemo.security;
                
                import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
                import com.demo.springSecurityFirstDemo.model.UserModel;
                import com.demo.springSecurityFirstDemo.service.UserService;
                import org.springframework.beans.factory.annotation.Autowired;
                import org.springframework.security.core.authority.AuthorityUtils;
                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 java.util.ArrayList;
                import java.util.List;
                
                /**
                 * @Author Java技术债务
                 * @Date 2022-03-22 22:19:51
                 * @Desc *
                 *
                 */
                @Service
                public class UserDetailsServiceImpl implements UserDetailsService {
                
                    @Autowired
                    private UserService userService;
                    /**
                     * Security的登录,User赋予权限
                     *
                     * @param userName
                     * @return
                     * @throws UsernameNotFoundException
                     */
                    @Override
                    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
                        System.out.println(userName);
                        QueryWrapper<UserModel> qw = new QueryWrapper<>();
                        qw.eq("user_name", userName);
                        UserModel userModel = userService.getOne(qw);
                        if (userModel == null) {
                            throw new UsernameNotFoundException("user not exist!");
                        }
                        SecurityUser securityUser = new SecurityUser();
                        securityUser.setId(userModel.getId());
                        securityUser.setUsername(userModel.getUserName());
                        securityUser.setPassword(userModel.getPassword());
                        securityUser.setAuthorityList(AuthorityUtils.commaSeparatedStringToAuthorityList(userModel.getRoles()));
                        securityUser.setEnable(userModel.getIsDeleted() == 0);
                
                        return securityUser;
                    }
                }
                

                其中AuthorityUtils.commaSeparatedStringToAuthorityList(userModel.getRoles()) 是将逗号拼接的权限字符串,比如:ROLE_USER,ROLE_ADMIN。

                SimpleGrantedAuthority是GrantedAuthority的一个实现类。Spring Security的权限几乎是用 SimpleGrantedAuthority生成的,只要注意每种角色对应一个GrantedAuthority即可。另外,一定要在自 己的UserDetailsService实现类上加入@Service注解,以便被Spring Security自动发现。

                [WebSecurityConfig.java](http://WebSecurityConfig.java) 完整代码

                package com.demo.springSecurityFirstDemo.security;
                
                import lombok.extern.slf4j.Slf4j;
                import org.springframework.beans.factory.annotation.Autowired;
                import org.springframework.context.annotation.Bean;
                import org.springframework.context.annotation.Configuration;
                import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
                import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
                import org.springframework.security.config.annotation.web.builders.HttpSecurity;
                import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
                import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
                import org.springframework.security.core.userdetails.User;
                import org.springframework.security.core.userdetails.UserDetailsService;
                import org.springframework.security.provisioning.InMemoryUserDetailsManager;
                import org.springframework.security.provisioning.JdbcUserDetailsManager;
                
                import javax.sql.DataSource;
                import java.io.PrintWriter;
                import java.sql.Connection;
                import java.sql.SQLException;
                import java.sql.SQLFeatureNotSupportedException;
                import java.util.logging.Logger;
                
                /**
                 * @author: Java技术债务
                 * @Date: 2021/6/5 18:45
                 * Describe: SpringSecurity配置
                 */
                @Configuration
                @EnableWebSecurity
                @EnableGlobalMethodSecurity(prePostEnabled = true)
                @Slf4j
                public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                
                    @Override
                    public void configure(HttpSecurity http) throws Exception {
                        http.authorizeRequests()
                                .antMatchers("/admin/test/**").hasRole("ADMIN")
                                .antMatchers("/user/test/**").hasRole("USER")
                                .antMatchers("/web/test/**").permitAll()
                                .anyRequest().authenticated()
                                .and()
                                .formLogin();
                
                    }
                }
                

                2.4 启动程序测试

                登录数据库保存的账号密码用户名:user密码:1权限role_user

                返回配置的成功页面

                在这里插入图片描述

                然后依次访问不同权限的接口

                • 访问/admin/test

                在这里插入图片描述

                • 访问/user/test

                在这里插入图片描述

                登录数据库保存的账号密码用户名:admin密码:1权限为role_admin和role_user

                访问/admin/test和/user/test/正常。

                如果登录不存在的用户测试结果如图。

                在这里插入图片描述

                在这里插入图片描述
                JVM内存泄漏和内存溢出的原因
                JVM常用监控工具解释以及使用
                Redis 常见面试题(一)
                ClickHouse之MaterializeMySQL引擎(十)
                三种实现分布式锁的实现与区别
                线程池的理解以及使用

                号外!号外!

                最近面试BAT,整理一份面试资料,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。想获取吗?如果你想提升自己,并且想和优秀的人一起进步,感兴趣的朋友,可以在扫码关注下方公众号。资料在公众号里静静的躺着呢。。。

                • 喜欢就收藏
                • 认同就点赞
                • 支持就关注
                • 疑问就评论

                一键四连,你的offer也四连

                ————————————————————————————————————————————————————————————————

                本文作者:Java技术债务
                原文链接:https://www.cuizb.top/myblog/article/1648393808
                版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。

                posted @   Java技术债务  阅读(75)  评论(0编辑  收藏  举报
                相关博文:
                阅读排行:
                · 周边上新:园子的第一款马克杯温暖上架
                · Open-Sora 2.0 重磅开源!
                · 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
                · Ollama——大语言模型本地部署的极速利器
                · DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
                点击右上角即可分享
                微信分享提示
                💬
                评论
                📌
                收藏
                💗
                关注
                👍
                推荐
                🚀
                回顶
                收起
                1. 1 404 not found REOL
                404 not found - REOL
                00:00 / 00:00
                An audio error has occurred.