欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Security入门级使用(示例)

程序员文章站 2024-03-22 23:01:04
...

软硬件环境

  • jdk1.8
  • IntelliJIdea
  • SpringBoot2.2.1.RELEASE
  • SpringSecurity5.2.1RELEASE

声明 本文主要示例入门级SpringSecurity,对其理论知识不作深入分析。

准备工作:提供以下API,以供之后的演示

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用于测试的controller层
 *
 * @author JustryDeng
 * @date 2019/11/26 10:05
 */
@Slf4j
@RestController
public class DemoController {

    /**
     * 未登录就可访问的页面
     *
     * 注: 用于.permitAll()测试。
     */
    @GetMapping("/hello")
    public String hello() {
        return "hello 靓仔~";
    }

    /**
     * 首页(登录成功后跳转至此页)
     *
     * 注:默认的,表单登录 登录成功时, 是以POST重定向至登陆成功页的,所以这里至少要支持POST请求。
     */
    @RequestMapping(value = "/index", method = {RequestMethod.GET, RequestMethod.POST})
    public String home() {
        return "欢迎来到index~";
    }

    /**
     * 登录失败页
     */
    @GetMapping("/login/failed")
    public String error() {
        return "登录失败~";
    }

    /**
     * 登出成功页
     */
    @GetMapping("/logout/success")
    public String logout() {
        return "您已成功退出~";
    }

    /**
     * 鉴权失败页
     */
    @GetMapping("/403")
    public String forbidden() {
        return "小伙~你的权限不够~";
    }

    @GetMapping("/user")
    public String user() {
        return "普通用户~";
    }

    @GetMapping("/dba")
    public String dba() {
        return "数据库DBA~";
    }

    @GetMapping("/admin")
    public String admin() {
        return "超级管理员~";
    }
}

Spring Security的入门级使用(示例)

第一步:在pom.xml中引入依赖

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

第二步:使用@EnableWebSecurity注解,启用WebSecurity

Spring Security入门级使用(示例)

第三步:自定义一个MyWebSecurityConfigurerAdapter类,继承WebSecurityConfigurerAdapter,自定义相关配置

提示一 这里主要以图片的形式进行说明,MyWebSecurityConfigurerAdapter类文字版的完整代码在本文末给出。

提示二 WebSecurityConfigurerAdapter类中,configure(AuthenticationManagerBuilder auth)方法、configure(HttpSecurity http)方法以及configure(WebSecurity web)方法相对常用,下面对这三个方法进行简单说明并重写示例。

  • 第一小步(可选) 重写configure(WebSecurity web)方法。

    1. 此方法相关的部分配置说明(未显示父类方法):
      Spring Security入门级使用(示例)
    2. (简单)示例:
      @Override
      public void configure(WebSecurity web) {
          /*
           * 对于那些没必要进行保护的资源, 可以使用ignoring,使其跳过SpringSecurity
           *
           * 注:configure(HttpSecurity http)方法里的permitAll();也有类似的效果,
           *    不过permitAll会走SpringSecurity,只是说无条件放行而已。
           */
          web.ignoring().antMatchers("/picture/**");
          web.ignoring().antMatchers("/md/**");
          // 开发时,可以将SpringSecurity的debug打开
          web.debug(true);
      }
      
  • 第二小步 重写configure(HttpSecurity http)方法。

    1. 此方法相关的部分配置说明(未显示父类方法):
      Spring Security入门级使用(示例)

    2. (简单的自定义鉴权配置)示例:

          /**
           * SpringSecurity提供有一些基本的页面(如:login、logout等);如果觉得它提供的
           * 基础页面难看,想使用自己的页面的话,可以在此方法里面进行相关配置。
           */
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              // 访问 匹配以下ant的url不需要(非匿名)认证、不需要鉴权
              http.authorizeRequests().antMatchers("/login", "/logout", "/logout/success", "/403", "/hello").permitAll();
      
              // 只要认证通过,就可访问匹配以下ant的url。 (不论这人的权限是什么)
              http.authorizeRequests().antMatchers("/index").authenticated();
      
              /*
               *  访问 匹配以下ant的url时, 需要至少有一个角色 "USER", "ADMIN"  (需要鉴权)
               *
               *  注:[鉴权] 这个动作里面就隐含[认证]了, 因为只有认证后,才能拿到权限信息,才能进行鉴权;
               *     如果连认证都没过的话,鉴权自然会失败。
               */
              http.authorizeRequests().antMatchers("/user").hasAnyRole("USER", "ADMIN", "abc");
              // 访问 匹配以下ant的url时, 需要至少有一个角色 "DBA", "ADMIN" (需要鉴权)
              http.authorizeRequests().antMatchers("/dba").hasAnyRole("DBA", "ADMIN");
              // 访问 匹配以下ant的url时, 需要有角色 "ADMIN" (需要鉴权)
              http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
      
              /*
               * 设置任何请求都需要认证(除了前面.permitAll()的)。
               *
               * 注:如果不设置此项的话,那么对于那些未作任何配置的URL, 那么是默认 不认证、不鉴权的
               */
              http.authorizeRequests().anyRequest().authenticated();
      
              // 设置登录方式为 表单登录
              http.formLogin();
              /// 设置登录方式为 弹框登录
              /// http.httpBasic();
              /// 自定义登录页
              /// http.formLogin().loginPage("myLoginPae");
              /// 自定义登出页
              /// http.logout().logoutUrl("myLogoutPae");
              // 登出成功时,跳转至此url
              http.logout().logoutSuccessUrl("/logout/success");
              // 登录成功时,跳转至此url
              // 注意:如果未登录,直接访问 登录失败页的话,会被DefaultLoginPageGeneratingFilter识别,并跳转至登录页进行登录
              http.formLogin().successForwardUrl("/index");
              // 登录失败时,跳转至此url
              // 注意:如果未登录,直接访问 登录失败页的话,会被DefaultLoginPageGeneratingFilter识别,并跳转至登录页进行登录
              http.formLogin().failureUrl("/login/failed");
              /// 当鉴权不通过,是 跳转至此url
              http.exceptionHandling().accessDeniedPage("/403");
          }
      

      注:上面代码中权限配置相关的方法,只示例了部分,完整的有:
      Spring Security入门级使用(示例)

  • 第三小步 重写configure(AuthenticationManagerBuilder auth)方法。

    1. 此方法相关的部分配置说明(未显示父类方法):
      Spring Security入门级使用(示例)

    2. (配置几个用户,进行简单)示例:

      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          // 配置几个用户
          auth.inMemoryAuthentication().withUser("user").password("user123").roles("USER");
          auth.inMemoryAuthentication().withUser("dba").password("dba123").roles("DBA");
          auth.inMemoryAuthentication().withUser("admin").password("admin123").roles("ADMIN");
      
          // 配置这个用户的目的,是为了说明: 角色名瞎**起都可以
          auth.inMemoryAuthentication().withUser("other").password("other123").roles("abc", "DBA");
      }
      

      注:角色名的定义并没有什么要求,瞎**写都可以。

第四步:注册一个自定义的PasswordEncoder,用于登录时密码比对

提示 本人这里为了快速简单演示,自定义了一个非常简单的PasswordEncoder实现;实际上,SpringSecurity对PasswordEncoder提供有大量实现,在实际开发时,如无特殊需求,完全可以使用SpringSecurity提供的PasswordEncoder实现类。

/**
 * 自定义 加密器
 *
 * 注:只需要将其注册进入容器中即可,InitializeUserDetailsBeanManagerConfigurer类会从容器
 *    拿去PasswordEncoder.class实现,作为其加密器
 *
 * @date 2019/12/21 17:59
 */
@Bean
public PasswordEncoder myPasswordEncoder() {
    return new PasswordEncoder() {
        @Override
        public String encode(CharSequence rawPassword) {
            return rawPassword == null ? "" : rawPassword.toString();
        }

        @Override
        public boolean matches(CharSequence rawPassword, String encodedPassword) {
            if (rawPassword == null || rawPassword.length() == 0) {
                return false;
            }
            return rawPassword.equals(encodedPassword);
        }
    };
}

启动项目,测试一下

测试流程

  1. 先访问/hello,能访问(证明permitAll生效)。
  2. 任意访问一个页面,会被转到登录页,进行登录。
  3. 登录成功,跳转至index(证明successForwardUrl("/index")生效)。
  4. 分别访问"/user"、"/dba"、"/admin"。发现自己有对应的角色才能访问进去,否者跳转至403页面(证明相关鉴权配置生效)。
  5. 登出,成功后,页面跳转至"/logout/success"(证明logoutSuccessUrl("/logout/success")生效)。

测试user用户
Spring Security入门级使用(示例)
测试dba用户
Spring Security入门级使用(示例)
测试admin用户
Spring Security入门级使用(示例)
测试other用户
Spring Security入门级使用(示例)
注:创建这个other用户,主要是为了说明,角色名瞎**起都可以(比如此用户就有一个名为abc的角色)。


给出MyWebSecurityConfigurerAdapter类完整版

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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * SpringSecurity配置
 *
 * @author JustryDeng
 * @date 2019/12/7 14:08
 */
@Configuration
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) {
        /*
         * 对于那些没必要进行保护的资源, 可以使用ignoring,使其跳过SpringSecurity
         *
         * 注:configure(HttpSecurity http)方法里的permitAll();也有类似的效果,
         *    不过permitAll会走SpringSecurity,只是说无条件放行而已。
         */
        web.ignoring().antMatchers("/picture/**");
        web.ignoring().antMatchers("/md/**");
        // 开发时,可以将SpringSecurity的debug打开
        web.debug(true);
    }

    /**
     * SpringSecurity提供有一些基本的页面(如:login、logout等);如果觉得它提供的
     * 基础页面难看,想使用自己的页面的话,可以在此方法里面进行相关配置。
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 访问 匹配以下ant的url不需要(非匿名)认证、不需要鉴权
        http.authorizeRequests().antMatchers("/login", "/logout", "/logout/success", "/403", "/hello").permitAll();

        // 只要认证通过,就可访问匹配以下ant的url。 (不论这人的权限是什么)
        http.authorizeRequests().antMatchers("/index").authenticated();

        /*
         *  访问 匹配以下ant的url时, 需要至少有一个角色 "USER", "ADMIN"  (需要鉴权)
         *
         *  注:[鉴权] 这个动作里面就隐含[认证]了, 因为只有认证后,才能拿到权限信息,才能进行鉴权;
         *     如果连认证都没过的话,鉴权自然会失败。
         */
        http.authorizeRequests().antMatchers("/user").hasAnyRole("USER", "ADMIN", "abc");
        // 访问 匹配以下ant的url时, 需要至少有一个角色 "DBA", "ADMIN" (需要鉴权)
        http.authorizeRequests().antMatchers("/dba").hasAnyRole("DBA", "ADMIN");
        // 访问 匹配以下ant的url时, 需要有角色 "ADMIN" (需要鉴权)
        http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");

        /*
         * 设置任何请求都需要认证(除了前面.permitAll()的)。
         *
         * 注:如果不设置此项的话,那么对于那些未作任何配置的URL, 那么是默认 不认证、不鉴权的
         */
        http.authorizeRequests().anyRequest().authenticated();

        // 设置登录方式为 表单登录
        http.formLogin();
        /// 设置登录方式为 弹框登录
        /// http.httpBasic();
        /// 自定义登录页
        /// http.formLogin().loginPage("myLoginPae");
        /// 自定义登出页
        /// http.logout().logoutUrl("myLogoutPae");
        // 登出成功时,跳转至此url
        http.logout().logoutSuccessUrl("/logout/success");
        // 登录成功时,跳转至此url
        // 注意:如果未登录,直接访问 登录失败页的话,会被DefaultLoginPageGeneratingFilter识别,并跳转至登录页进行登录
        http.formLogin().successForwardUrl("/index");
        // 登录失败时,跳转至此url
        // 注意:如果未登录,直接访问 登录失败页的话,会被DefaultLoginPageGeneratingFilter识别,并跳转至登录页进行登录
        http.formLogin().failureUrl("/login/failed");
        /// 当鉴权不通过,是 跳转至此url
        http.exceptionHandling().accessDeniedPage("/403");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 配置几个用户
        auth.inMemoryAuthentication().withUser("user").password("user123").roles("USER");
        auth.inMemoryAuthentication().withUser("dba").password("dba123").roles("DBA");
        auth.inMemoryAuthentication().withUser("admin").password("admin123").roles("ADMIN");

        // 配置这个用户的目的,是为了说明: 角色名瞎**起都可以
        auth.inMemoryAuthentication().withUser("other").password("other123").roles("abc", "DBA");
    }

    /**
     * 自定义 加密器
     *
     * 注:只需要将其注册进入容器中即可,InitializeUserDetailsBeanManagerConfigurer类会从容器
     *    拿去PasswordEncoder.class实现,作为其加密器
     *
     * @date 2019/12/21 17:59
     */
    @Bean
    public PasswordEncoder myPasswordEncoder() {
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence rawPassword) {
                return rawPassword == null ? "" : rawPassword.toString();
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                if (rawPassword == null || rawPassword.length() == 0) {
                    return false;
                }
                return rawPassword.equals(encodedPassword);
            }
        };
    }
}

入门级Spring Security学习完毕 !


^_^ 如有不当之处,欢迎指正

^_^ 参考资料
        《SpringSecurity5.2.1RELEASE源码》

^_^ 测试代码托管链接
         https://github.com/JustryDeng…SpringSecurity…

^_^ 本文已经被收录进《程序员成长笔记(六)》,笔者JustryDeng

相关标签: 权限框架