• 微服务 第九章 springboot 使用Spring Security 安全验证


    1、在springboot基础上添加maven:

     <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>5.3.1.RELEASE</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>5.3.1.RELEASE</version>
            </dependency>
    

    2、启动,启动日志:Using generated security password: 9141f795-55b8-4433-ae85-77c47a56e079   用户名是user

    3、url请求,重定向到服务认证,输入用户名密码,认证成功后重定向到请求的url。再次访问请求url,不再认证。访问urlhttp://localhost:8399/logout 退出。

     4、改密码:

    在application.properties添加

    spring.security.user.name=yaohuiqin
    spring.security.user.password=123
    
    @SpringBootApplication
    /*@EnableAutoConfiguration(exclude = {
            org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class
    })*/
    public class SpringsecurityApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringsecurityApplication.class, args);
        }
    
    }
    

    再启动项目,则可以用用户名yaohuiqin,密码123登录。

    换一种方式:删除application.properties文件中的用户名密码,添加类:

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Bean
        PasswordEncoder passwordEncoder() {
            return NoOpPasswordEncoder.getInstance();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("yhq")
                    .password("123").roles("admin");
        }
    } 

    换一种方式

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Bean
        PasswordEncoder passwordEncoder() {
            return NoOpPasswordEncoder.getInstance();
        }
        
        @Bean
        protected UserDetailsService userDetailsService() {
            InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
            manager.createUser(User.withUsername("wxh").password("123").roles("admin").build());
            manager.createUser(User.withUsername("yhquser").password("123").roles("user").build());
            return manager;
        }
    }
    

      

    5、表单登录配置

    http.formLogin()
                    .loginPage("/login.html")
                    .loginProcessingUrl("/doLogin")
                    .usernameParameter("name")
                    .passwordParameter("passwd")
                    .defaultSuccessUrl("/index")
                    .successForwardUrl("/index")
                    .permitAll()
                    .and()
                    .logout()
                    .logoutUrl("/logout")
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout","POST"))
                    .logoutSuccessUrl("/index")
                    .deleteCookies()
                    .clearAuthentication(true)
                    .invalidateHttpSession(true)
                    .permitAll();
    

      注销登录的配置:

    1. 默认注销的 URL 是 /logout,是一个 GET 请求,我们可以通过 logoutUrl 方法来修改默认的注销 URL。
    2. logoutRequestMatcher 方法不仅可以修改注销 URL,还可以修改请求方式,实际项目中,这个方法和 logoutUrl 任意设置一个即可。
    3. logoutSuccessUrl 表示注销成功后要跳转的页面。
    4. deleteCookies 用来清除 cookie。
    5. clearAuthentication 和 invalidateHttpSession 分别表示清除认证信息和使 HttpSession 失效,默认可以不用配置,默认就会清除。

     6、Spring Security 结合 Jwt 实现无状态登录

      6.1 jwt认证和session认证

        JWT,全称是 Json Web Token , 是一种 JSON 风格的轻量级的授权和身份认证规范,可实现无状态、分布式的 Web 应用授权:

          JWT属于无状态认证,支持集群化部署,服务端可以任意迁移,减少服务端存储session压力,多平台跨域。流程:用户发送用户名和密码,服务端验证成功后用户信息加密并且编码成一个 token给客户端,客户端每次请求携带token,服务端接收请求时会解密token再验证token是否有,效获取用户登录信息,再根据授权获取受保护的资源

          有以下几个方法可以做到失效 JWT token(后期再考虑不同方式的优缺点)

    1. 将 token 存入 DB(如 Redis)中,失效则删除;但增加了一个每次校验时候都要先从 DB 中查询 token 是否存在的步骤,而且违背了 JWT 的无状态原则(这不就和 session 一样了么?)。
    2. 维护一个 token 黑名单,失效则加入黑名单中。
    3. 在 JWT 中增加一个版本号字段,失效则改变该版本号。
    4. 在服务端设置加密的 key 时,为每个用户生成唯一的 key,失效则改变该 key。

     7、Spring Security 配置

    在前后端分离这样的开发架构下,前后端的交互都是通过 JSON 来进行,无论登录成功还是失败,都不会有什么服务端跳转或者客户端跳转之类。

    登录成功了,服务端就返回一段登录成功的提示 JSON 给前端,前端收到之后,该跳转该展示,由前端自己决定,就和后端没有关系了。

    登录失败了,服务端就返回一段登录失败的提示 JSON 给前端,前端收到之后,该跳转该展示,由前端自己决定,也和后端没有关系了。

    package com.yhq.springsecurity.config;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.context.annotation.Bean;
    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.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.factory.PasswordEncoderFactories;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.provisioning.InMemoryUserDetailsManager;
    import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
    
    import java.io.PrintWriter;
    
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //不同情况下的配置,用来记录
            http.authorizeRequests()
                    .antMatchers("/", "/index").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/loginindex")
                    .permitAll()
                    .and()
                    .logout()
                    .permitAll();
    
    
            http.formLogin()
                    .loginPage("/login.html")
                    .loginProcessingUrl("/doLogin")
                    .usernameParameter("name")
                    .passwordParameter("passwd")
                    .defaultSuccessUrl("/index")
                    .successForwardUrl("/index")
                    .permitAll()
                    .and()
                    .logout()
                    .logoutUrl("/logout")
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "POST"))
                    .logoutSuccessUrl("/index")
                    .deleteCookies()
                    .clearAuthentication(true)
                    .invalidateHttpSession(true)
                    .permitAll();
    
            //统统 JSON 交互
            http.formLogin().successHandler((req, resp, authentication) -> {
                Object principal = authentication.getPrincipal();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                out.write(new ObjectMapper().writeValueAsString(principal));
                out.flush();
                out.close();
            }).and().formLogin().failureHandler((req, resp, e) -> {
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                out.write(e.getMessage());
                out.flush();
                out.close();
            }).and().csrf().disable().exceptionHandling()
                    .authenticationEntryPoint((req, resp, authException) -> {
                                resp.setContentType("application/json;charset=utf-8");
                                PrintWriter out = resp.getWriter();
                                out.write("尚未登录,请先登录");
                                out.flush();
                                out.close();
            }).and().logout()
                    .logoutUrl("/logout")
                    .logoutSuccessHandler((req, resp, authentication) -> {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        out.write("注销成功");
                        out.flush();
                        out.close();
                    })
                    .permitAll();
    
    
        }
    
        @Bean
        @Override
        public UserDetailsService userDetailsService() {
            UserDetails user =
                    User.withDefaultPasswordEncoder()
                            .username("user")
                            .password("password")
                            .roles("USER")
                            .build();
            PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
    
            UserDetails user2 = User
                    .withUsername("yun")
                    .password(passwordEncoder.encode("123456"))
                    .roles("USER")
                    .build();
            return new InMemoryUserDetailsManager(user2);
        }
    }
    

      

  • 相关阅读:
    lookup:ID列
    分享几篇文章
    怎样无限制使用smartgit ?
    C++ Win32控制台应用程序捕捉关闭事件
    mt4 在K线上 放文字
    变色指标
    用windows 打包 证书
    监管fca asic nfa 啥啥啥
    sublime 3 build 3126 code ,压缩包在我的360企业云盘里,搜sublime
    个人作业收官——软件工程实践总结
  • 原文地址:https://www.cnblogs.com/yaohuiqin/p/12619951.html
Copyright © 2020-2023  润新知