1 Star 0 Fork 29

tracese / notebook

forked from JustryDeng / notebook 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
[02]Spring Security入门级使用(示例).md 18.84 KB
一键复制 编辑 原始数据 按行查看 历史
邓沙利文 提交于 2022-08-03 17:43 . 优化author

Spring Security入门级使用(示例)


软硬件环境

  • 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 <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @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的入门级使用(示例)

第一步:引入依赖

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

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

在这里插入图片描述

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

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

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

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

    1. 此方法相关的部分配置说明(未显示父类方法):

      在这里插入图片描述

    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. 此方法相关的部分配置说明(未显示父类方法):

      在这里插入图片描述

    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");
      }

      注:上面代码中权限配置相关的方法,只示例了部分,完整的有:

      在这里插入图片描述

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

    1. 此方法相关的部分配置说明(未显示父类方法):

      在这里插入图片描述

    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用户:

在这里插入图片描述

测试dba用户:

在这里插入图片描述

测试admin用户:

在这里插入图片描述

测试other用户:

在这里插入图片描述

注:创建这个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 <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @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学习完毕 !

相关资料

1
https://gitee.com/Trace001/notebook.git
git@gitee.com:Trace001/notebook.git
Trace001
notebook
notebook
master

搜索帮助