上一篇:https://blog.csdn.net/LawssssCat/article/details/105065992

下一篇:https://lawsssscat.blog.csdn.net/article/details/105080690

Hello World

SpringBoot集成Spring Security入门体验

<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>
@RestController
public class IndexController {
    @GetMapping("/index")
    public String index() {
        return "Hello World ~";
    }
}

运行项目访问 http://127.0.0.1:8080/index

温馨小提示:在不进行任何配置的情况下,Spring Security 给出的默认用户名为 user 密码则是项目在启动运行时随机生成的一串字符串,会打印在控制台,如下图:

认证成功之后才会跳转到我们的index页面

配置用户密码

有很多方式,静态写死、从数据库中取、rest方式从第三方接口中取…

下面讲两种静态写死的方法

  1. 直接写在application.yml里
spring:
  security:
    user:
      name: admin  # 用户名
      password: 123456 # 密码

或者

  1. 新建Security <mark>核心</mark> 配置类继承 WebSecurityConfigurerAdapter (建议)
package com.example.securitydemo.config;

import org.apache.tomcat.util.security.MD5Encoder;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/** * @author alan smith * @version 1.0 * @date 2020/3/24 15:36 */
@Configuration
// 启动 spring security 的 web 安全支持
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /** * 将用户设置在内存中 * * @param auth * @throws Exception */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 获取加密者
        PasswordEncoder passwordEncoder = passwordEncoder();

        // 在内存中写入用户信息
        auth.inMemoryAuthentication()
                // 指定加密方式
                .passwordEncoder(passwordEncoder)
                .withUser("admin").password(passwordEncoder.encode("123456")).roles("ADMIN")
                .and()
                .withUser("test").password(passwordEncoder.encode("111111")).roles("USER");
    }

    private PasswordEncoder passwordEncoder() {
        // return new MessageDigestPasswordEncoder("MD5")
        // or
        // BCryptPasswordEncoder: Spring Security 提供的加密工具,可快速实现加密加盐
        return new BCryptPasswordEncoder();
    }
}

admin/123456 访问并登录 localhost:8080/index (成功)

注意:因为没有指定默认的登录成功后转跳的地址,那么默认是 /
也是说,如果你 访问的是 localhost:8080/login
那么,登录成功后会转跳到 localhost:8080/ 地址(而这个地址我们没有定义,因此就报错了)

  1. 从数据库中获取用户账号、密码信息
    这种方式也就是我们项目中通常使用的方式,这个留到 <mark>后面的文章再说</mark>

登录处理 与 忽略拦截

package com.example.securitydemo.controller;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/** * @author alan smith * @version 1.0 * @date 2020/3/24 15:28 */
@RestController
public class IndexController {

    @GetMapping("/index")
    public String index() {
        return "hello world";
    }

    @GetMapping("/home")
    public String home() {
        return "home";
    }

    @GetMapping("/getUserInfo")
    public Object userInfo() {
        return SecurityContextHolder.getContext();
    }
}

重点!

修改 Security <mark>核心</mark> 类 ,重写 三个 configure 方法

package com.example.securitydemo.config;

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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.io.PrintWriter;

/** * {@link WebSecurityConfigurerAdapter}: * spring security 提供的 {@link WebSecurity} 适配器 * 一个帮我们实现了大部分{@link WebSecurityConfigurer}接口的抽象类 * <p> * 提供了可以重写的configure方法,在方法内可以装配自定义的Filter配置 * 其指定的Filter会被生产为{@link FilterChainProxy} * 最终以{@link DelegatingFilterProxy}作为spring Security Filter Chain起作用 * <p> * 即:其实现是用来配置spring Security Filter Chain的 * * @author alan smith * @version 1.0 * @date 2020/3/24 15:36 */
@Configuration
// 启动 spring security 的 web 安全支持
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /** * 将用户设置在内存中 * * @param auth 封装认证授权管理器 * @throws Exception */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 获取加密者
        PasswordEncoder passwordEncoder = passwordEncoder();

        // 在内存中写入用户信息
        auth.inMemoryAuthentication()
                // 指定加密方式
                .passwordEncoder(passwordEncoder)
                // 录入 admin 用户信息,自定义 ADMIN 权限
                .withUser("admin").password(passwordEncoder.encode("123456")).roles("ADMIN")
                .and()
                // 录入 test 用户信息,自定义 USER 权限
                .withUser("test").password(passwordEncoder.encode("111111")).roles("USER");
    }

    private PasswordEncoder passwordEncoder() {
        // return new MessageDigestPasswordEncoder("MD5")
        // or
        // BCryptPasswordEncoder: Spring Security 提供的加密工具,可快速实现加密加盐
        return new BCryptPasswordEncoder();
    }

    /** * 对Filter的配置 * * @param web 对web请求的封装,可以操作Filter * @throws Exception */
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 设置拦截忽略 url - 会直接过滤改url - 将不会经过 Spring Security 过滤器链
        web
                // 设置拦截忽略文件夹,请求 url 匹配则不拦截
                .ignoring().antMatchers("/favicon.ico")
                .and()
                // 可以对静态资源放行
                .ignoring().antMatchers("/css/**", "/js/**");
    }

    /** * 对FilterChain的配置 * * @param http 对http请求和响应的封装,可以操作FilterChain * @throws Exception */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 开启登录匹配
        http.authorizeRequests()
                // 标识访问 '/index' 这个接口,需要具备 ADMIN 角色
                .antMatchers("/index").hasRole("ADMIN")
                // 设置可匿名匿名访问的标识
                .antMatchers("/", "/home").permitAll()
                // 其余所有请求都需要认证
                .anyRequest().authenticated()

                .and()

                // 设置登录认证页面
                .formLogin()
                // 配置(自定义的)登录页面的 url
                //.loginPage("/login")
                // 自定义登录用户名和密码的属性名,默认为 username 和 password
                .usernameParameter("username1")
                .passwordParameter("password1")
                // 登录后的(默认)转跳 - 方式1
                .loginProcessingUrl("/home")
                // 登录后的(默认)转跳 - 方式2
                .successHandler((req, resp, authentication) -> {
                    //resp.sendRedirect("/home");
                    System.out.println(SecurityContextHolder.getContext());
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    out.write("登录成功...");
                    out.flush();
                })
                // 配置登录失败的回调 - 方式1
                //.failureForwardUrl("/home")
                // 配置登录失败的回调 - 方式2
                .failureHandler((req, resp, exception) -> {
                    resp.setContentType("application/json;carset=utf-8");
                    PrintWriter out = resp.getWriter();
                    out.write("登录失败...");
                    out.flush();
                })
                // 和表单登录相关的接口统统都自接通过
                .permitAll()

                .and()

                .logout()
                .logoutUrl("/logout")
                // 配置注销成功的回调
                .logoutSuccessHandler((req, resp, authentication) -> {
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    out.write("注销成功...");
                    out.flush();
                })
                .permitAll()

                .and()
                // 开启 http basic 认证
                .httpBasic()
                .and()
                // 关闭 csrf 跨域
                .csrf().disable();


    }
}

未登录

http://localhost:8080/home (无需登陆)

下面两个,都会转跳到 /login 页面
http://localhost:8080/getUserInfo (需登陆)
http://localhost:8080/index (ADMIN 权限)

admin/123456 登陆后

admin 登陆后
http://localhost:8080/index (ADMIN 权限)

http://localhost:8080/getUserInfo (需登陆)

http://localhost:8080/logout (登出)

test/111111 登陆

http://localhost:8080/login

http://localhost:8080/getUserInfo (需登陆)

http://localhost:8080/index (ADMIN 权限)
【失败:因为当前账号不是 ADMIN 权限】

done

Github 下载


归档

# 参考

# 核心配置图