• SpringSecurity的使用


    介绍

    Spring Security 是一个高度自定义的安全框架。利用 Spring IoC/DI 和 AOP 功能,为系统提供了声明式安全访问控制功能,减少了为系 统安全而编写大量重复代码的工作。主要实现两个功能:

    1. 用户登录的控制
    2. 登录后权限的控制

    使用

    引入依赖

     <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
     </dependency>
    

    引入了依赖,其实就已经完成了配置,当我请求我们所要进入的页面时,都会先进行登录的验证,才会跳转到我们访问的页面。但是在实际的使用中,我们一般会跳转到我们请求的页面做安全的控制以及权限的控制,因此我们需要自定义对应的类。

    UserDetailsService接口

    当什么也没有配置的时候,账号和密码是由 Spring Security 定义 生成的。而在实际项目中账号和密码都是从数据库中查询出来的。所 以我们要通过自定义逻辑控制认证逻辑。

    三个参数具体解释:

    username:用户名,这里默认提交表单中的 name 必须叫username

    password:密码

    authorities:用户具有的权限。此处不允许为 null

    此处的用户名应该是客户端传递过来的用户名。而密码应该是从 数据库中查询出来的密码。Spring Security 会根据 User 中的 password 和客户端传递过来的 password 进行比较。如果相同则表示认证通过, 如果不相同表示认证失败。authorities 里面包含的所有内容为此用户具有的权限,如有里面没有包含某个权限,而在做 某个事情时必须包含某个权限则会出现 403。通常都是通过 AuthorityUtils.commaSeparatedStringToAuthorityList(“”) 来 创 建 authorities 集合对象的。参数时一个字符串,多个权限使用逗号分隔。

    如果需要自定义逻辑时,只需要实现 UserDetailsService 接口即可,就可以进行对应的登录配置。

    BCryptPasswordEncoder

    BCryptPasswordEncoder 是 Spring Security 官方推荐的密码解析器,平时多使用这个解析器。BCryptPasswordEncoder 是对 bcrypt 强散列方法的具体实现。是 基于 Hash 算法实现的单向加密。

    encode():把参数按照特定的解析规则进行解析。

    matches()验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回 true;如果不匹配,则返回 false。 第一个参数表示需要被解析的密码。第二个参数表示存储的密码。

    //加密的使用
    public void test(){
            BCryptPasswordEncoder pe = new BCryptPasswordEncoder();
            String encode = pe.encode("123");//加密
            System.out.println(encode);
    
            boolean matches = pe.matches("1234", encode);//原数据和加密匹配
            System.out.println(matches);//false
        }
    

    自定义登录逻辑

    当进行自定义登录逻辑时需要用到之前讲解的 UserDetailsService 和 PasswordEncoder。但是 Spring Security 要求:当进行自定义登录逻辑时容器内必须有 PasswordEncoder 实例。因此通过创建配置类注入该对象

    • PasswordEncoder配置类
    @Configuration
    public class SecurityConfig{
     @Bean
        public PasswordEncoder getPe() {
            return new BCryptPasswordEncoder();
        }
    }
    
    • 编写登录用户逻辑,需要实现UserDetailsService
    @Service
    public class UserDetailServiceImpl implements UserDetailsService {
    
        @Autowired
        private PasswordEncoder encoder;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //1.查询数据库,查询用户名是否存在,如果不存在抛出UsernameNotFoundException
            if (!username.equals("admin")){
                throw new  UsernameNotFoundException("用户名不存在");
            }
            //2.把查询出来的密码进行解析,或直接把构造方法放到构造方法中
            //password 就是数据库中查询出来的密码,查询内容不是 123
            String password = encoder.encode("123");//加密后的数据,这里是模拟数据库中的密码
          //三个参数:用户名,数据库加密后的密码,实现的权限,角色,可访问的页面
            return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_adminN,/main.html,/main1.html"));
        }
    }
    

    自定义页面逻辑

    简单说就是,你要设置那个页面做主页,那些页面不用验证,那些页面需要认证,那个角色或者权限能访问那个页面

    该。配置类需要继承 WebSecurityConfigurerAdapte,并重写 configure 方法。

    • 常用的配置中调用的方法

      successForwardUrl()登录成功后跳转地址,使用 successForwardUrl()时表示成功后转发请求到地址。内部是通过 successHandler()方法进行控制成功后交给哪个类进行处理。ForwardAuthenticationSuccessHandler 内部就是最简单的请求转发。由于是请求转发,当遇到需要跳转到站外或在前后端分离的项目中就无法使用了。

      当需要控制登录成功后去做一些事情时,可以进行自定义认证成功控制器。这里以成功控制器为例,自定义认证失败控制器只需要继承AuthenticationFailureHandler即可

      //自定义登录成功处理器
      public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
          private String url;
      
          public MyAuthenticationSuccessHandler(String url) {
              this.url = url;
          }
      
          @Override
          public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
              System.out.println(httpServletRequest.getRemoteAddr());
              User user = (User) authentication.getPrincipal();
              System.out.println(user.getPassword());//默认 null
              System.out.println(user.getAuthorities());//权限
              httpServletResponse.sendRedirect(url);
          }
      }
      

      loginPage() 登录页面

      loginProcessingUrl 登录页面表单提交地址,此地址可以不真实存在。

      antMatchers():匹配内容 permitAll():允许

      @Configuration
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Autowired
          private MyAccessDeniedHandler myAccessDeniedHandler;
      
          @Autowired
          private UserDetailServiceImpl userDetailService;
      
          @Autowired
          private DataSource dataSource;
      
          @Autowired
          private PersistentTokenRepository repository;
      
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              System.out.println("执行UserDetailServiceImpl");
              //表单认证(登录)
              http.formLogin()
                      .loginProcessingUrl("/login")//当发现/login时认为是登录,需要执行UserDetailServiceImpl
                       .successForwardUrl("/toMain")//此处是一个 post 请求不能写.html 页面,静态资源只能走 get 请求
                      //自定义跳转
                     // .successHandler(new MyAuthenticationSuccessHandler("/main.html"))
                      .failureHandler(new MyAuthenticationFailHandler("/fail.html"))
                      //.failureForwardUrl("/fail")
                      .loginPage("/showLogin");//登录页面
      
      
              //url 拦截(授权)
              http.authorizeRequests()
                      .antMatchers("/showLogin", "/fail.html").permitAll()//login.html 不需要被认证
                      // .mvcMatchers("/login.html").servletPath("/hello").permitAll()
                     // .antMatchers("/main1.html").hasIpAddress("0:0:0:0:0:0:0:1")//具有其中一个就能访问
                      //.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)");//所有的请求都必须被认证,也就是必须登录后才能访问
                      .anyRequest().authenticated();
      
              //关闭 csrf
             // http.csrf().disable();
      
              //异常
              http.exceptionHandling()
                      .accessDeniedHandler(myAccessDeniedHandler);
      
              http.rememberMe()
                      //.tokenValiditySeconds()设置有效时间默认 2 周
                      .userDetailsService(userDetailService)//用户登录逻辑写在那个对象中
                      .tokenRepository(repository);
      
              //退出
              http.logout()
                      .logoutSuccessUrl("/showLogin");
          }
      
      
          @Bean
          public PersistentTokenRepository getPer(){
              JdbcTokenRepositoryImpl jt = new JdbcTokenRepositoryImpl();
              jt.setDataSource(dataSource);//需要一个数据源
              //jt.setCreateTableOnStartup(true);//第一次需要使用
              return jt;
          }
      
    悲观者正确,乐观者成功
  • 相关阅读:
    正确解读free -m
    linux命令总结之traceroute命令
    OSI七层模型详解
    Linux运维七:网络基础
    python contextlib 上下文管理器
    Django扩展自定义manage命令
    Elasticsearch分片、副本与路由(shard replica routing)
    EsRejectedExecutionException排错与线程池类型
    python重试(指数退避算法)
    Redis实现分布式锁
  • 原文地址:https://www.cnblogs.com/freebule/p/14462626.html
Copyright © 2020-2023  润新知