美文网首页
spring-security 入门教程

spring-security 入门教程

作者: ithankzc | 来源:发表于2021-11-21 13:25 被阅读0次

    引入 spring-security jar 包,不做任何额外配置

    启动服务自动生成的随机密码

    Using generated security password: 52713582-35cf-4b0c-832a-1342faece243
    

    默认登录页

    image.png

    实际业务,会存在多用户的的情况,spring-security 也提供在内存及数据库管理用户的方法。为了方便,我们可以先尝试内存操作

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/login").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .defaultSuccessUrl("/user")  //登录成功后跳转地址
                    .and().httpBasic();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().passwordEncoder(customPasswordEncoder())
                    .withUser("cxc").password(customPasswordEncoder().encode("cxcpwd")).roles("USER");
        }
    
        @Bean
        PasswordEncoder customPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
    

    写一个 reset 请求进行验证

    @RestController
    public class IndexController {
    
        @GetMapping("/hello")
        String hello() {
            return "hello world";
        }
    
        @GetMapping("/user")
        String user() {
            return SecurityContextHolder.getContext().getAuthentication().getName();
        }
    }
    

    登录 & 验证效果


    image.png

    稍微高级一点的用法

    定义一个 authenticationProvider,然后在 configure 将这个 provider 作为参数传入到 auth.authenticationProvider(authenticationProvider());

        @Bean
        public DaoAuthenticationProvider authenticationProvider(){
            DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
            provider.setPasswordEncoder(customPasswordEncoder());
           // 这里直接还是用默认的 InMemoryUserDetailsManager,就不再实现 UserDetailsService 接口
            provider.setUserDetailsService(new InMemoryUserDetailsManager());
            return provider;
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().passwordEncoder(customPasswordEncoder())
                    .withUser("chenxiaochi").password(customPasswordEncoder().encode("pwd")).roles("USER");
            auth.authenticationProvider(authenticationProvider());
        }
    

    项目用到的 pom 文件

    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.5.7</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.cxc</groupId>
        <artifactId>security</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>security</name>
        <description>security</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    重点讲解部分

    PasswordEncoder,UserDetailsService 的初始化

    如果没注入自定义的 provider , InitializeUserDetailsManagerConfigurer 会接管初始化配置,此时 UserDetailsService,PasswordEncoder 将会被注入系统默认的 provider

    InitializeUserDetailsManagerConfigurer

        public void init(AuthenticationManagerBuilder auth) throws Exception {
            auth.apply(new InitializeUserDetailsBeanManagerConfigurer.InitializeUserDetailsManagerConfigurer());
        }
    
        class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
            InitializeUserDetailsManagerConfigurer() {
            }
    
            public void configure(AuthenticationManagerBuilder auth) throws Exception {
                if (!auth.isConfigured()) {
                    UserDetailsService userDetailsService = (UserDetailsService)this.getBeanOrNull(UserDetailsService.class);
                    if (userDetailsService != null) {
                        PasswordEncoder passwordEncoder = (PasswordEncoder)this.getBeanOrNull(PasswordEncoder.class);
                        UserDetailsPasswordService passwordManager = (UserDetailsPasswordService)this.getBeanOrNull(UserDetailsPasswordService.class);
                        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
                        provider.setUserDetailsService(userDetailsService);
                        if (passwordEncoder != null) {
                            provider.setPasswordEncoder(passwordEncoder);
                        }
    
                        if (passwordManager != null) {
                            provider.setUserDetailsPasswordService(passwordManager);
                        }
    
                        provider.afterPropertiesSet();
                        auth.authenticationProvider(provider);
                    }
                }
            }
    
            private <T> T getBeanOrNull(Class<T> type) {
                String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanNamesForType(type);
                return beanNames.length != 1 ? null : InitializeUserDetailsBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
            }
        }
    

    AuthenticationManagerBuilder

        public boolean isConfigured() {
            return !this.authenticationProviders.isEmpty() || this.parentAuthenticationManager != null;
        }
    

    所以尽量在注入PasswordEncoder的时候,尽量隐藏密码类型细节,返回类型为接口类型即可。

        @Bean
        PasswordEncoder customPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    

    总结

    这里不涉及太多源代码研究,更多的是演示怎样用,一个简单的例子感受 spring-security。

    参考

    网上好的例子: https://juejin.cn/post/6844903896687575047
    网上好的例子2: https://progressivecoder.com/implementing-spring-boot-security-using-userdetailsservice/
    验证流程分析文章 https://juejin.cn/post/6844903806921244679

    说点其他的

    公司项目因业务原因,没有过多依赖于 spring-security 的用户名及密码校验,主要用了认证成功的登录态设置,权限。是通过过滤器的方式处理的。 所以也会写一篇 spring-security 过滤器的。

    相关文章

      网友评论

          本文标题:spring-security 入门教程

          本文链接:https://www.haomeiwen.com/subject/czpytrtx.html