添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

初识Spring Security

Spring Security简介

应用程序的安全性通常体现在两个方面:认证和授权。
1、认证:是确认某主体在某系统中是否合法、可用的过程。这里的主体既可以是登录系统的用户,也可以是接入的设备或者其他系统。
2、授权:指当主题通过认证之后,是否允许其执行某项操作的过程。

Spring Security已经集成的认证技术:

  • HTTP BASIC authentication headers:一个基于IETF RFC的标准;
  • HTTP Digest authentication headers:一个基于IETF RFC的标准;
  • HTTP X.509 client certificate excahnge:一个基于IETF RFC的标准;
  • LDAP:一种常见的跨平台身份验证方式;
  • Form-based authentication:用于简单的用户界面需求;
  • OpenID authentication:一种去中心化的身份认证方式;
  • Authentication based on pre-established request headers:类似于Computer Associates SiteMinder,一种用户身份验证及授权的集中式安全基础方案;
  • Jasig Central Authentication Service:单点登录方案;
  • Transparent authentication context propagation for Remote Method Invocation (RMI) and HttpInvoker:一个Spring远程调用协议;
  • Automatic “remember-me” authentication:允许在指定到期时间前自行重新登录系统;
  • Anonymous authentication:允许匿名用户使用特定的身份安全访问资源;
  • Run-as authentication:允许在一个会话中变换用户身份的机制;
  • Java Authentication and Authorization Service:JAAS,Java验证和授权API;
  • Java EE container authentication:允许系统继续使用容器管理这种身份验证方式;
  • Kerberos:一种使用对称密钥机制,允许客户端与服务器相互确认身份的认证协议。
  • 除此之外,Spring Security还引入了一些第三方包,用于支持更多的认证技术,如JOSSO等。如果所有这些技术都无法满足需求,则Spring Security允许我们编写自己的认证技术。因此,在绝大部分情况下,当我们有Java应用安全方面的需求时,选择Spring Security往往是正确而有效的。

    在授权上,Spring Security不仅支持基于URL对Web的请求授权,还支持方法访问授权、对象访问授权等,基本涵盖常见的大部分授权场景。

    创建一个简单的Spring Security项目

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    点进spring-boot-starter-security,可以看到其依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.5.5</version>
    <scope>compile</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.10</version>
    <scope>compile</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.5.2</version>
    <scope>compile</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.5.2</version>
    <scope>compile</scope>
    </dependency>
    </dependencies>

    简单测试——默认生效的HTTP基本认证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package com.louris.springboot;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @SpringBootApplication
    public class Application {

    @GetMapping("/")
    public String hello(){
    return "hello, spring security";
    }

    public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
    }

    }

    进入 localhost:8080 后跳出登录页面验证,默认用户名为user, 密码为动态生成的,需要查看后台控制台信息。验证通过后才进入页面。

    可以通过配置文件设置:

    1
    2
    spring.security.user.name=user
    spring.security.user.password=123456

    默认表单认证

    自定义WebSecurityConfiguerAdapter类

    1
    2
    3
    4
    5
    6
    7
    8
    package com.louris.springboot.config;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

    @EnableWebSecurity //里面已经加入了@Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    }

    其中WebSecurityConfiguerAdapter类中的方法已经声明了一些安全特性:

  • 验证所有请求;
  • 允许用户使用表单登录进行身份验证(Spring Security提供了一个简单的表单登录页面);
  • 允许用户使用HTTP基本认证;
    1
    2
    3
    4
    5
    6
    7
    8
    protected void configure(HttpSecurity http) throws Exception {
    this.logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
    http.authorizeRequests((requests) -> {
    ((AuthorizedUrl)requests.anyRequest()).authenticated();
    });
    http.formLogin();
    http.httpBasic();
    }

    重新刷新的话,直接进入页面了,没有表单验证,需要清理缓存。

    自定义表单登录页

    初步配置自定义表单登录页

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package com.louris.springboot.config;
    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;

    @EnableWebSecurity //里面已经加入了@Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
    .anyRequest().authenticated() //匹配系统的URL,并为其制定安全策略
    .and()
    .formLogin() //需要Spring Security提供的表单认证方式,返回对应的配置器
    .loginPage("/myLogin.html") //指定自定义的登录页面
    .permitAll() //使登录页不设限访问
    .and()
    .csrf() //Spring Security提供的跨站请求伪造防护功能
    .disable();
    }
    }

    将页面放在 resources/static

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" http-equiv="Content-Type" content="text/html">
    <title>登录</title>
    </head>
    <body>
    <div class="login">
    <h2>Acced Form</h2>
    <div class="login-top">
    <h1>LOGIN FORM</h1>
    <form action="myLogin.html" method="post">
    <input type="text" name="username" placeholder="username"/>
    <input type="password" name="password" placeholder="password"/>
    <div class="forgot">
    <a href="#">forgot Password</a>
    <input type="submit" value="Login">
    </div>
    </form>
    </div>
    <div class="login-bottom">
    <h3>New User &nbsp;<a href="#">Register</a>&nbsp Here</h3>
    </div>
    </div>
    </body>
    </html>

    其他表单配置项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    package com.louris.springboot.config;
    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.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;

    @EnableWebSecurity //里面已经加入了@Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    //指定登录成功时的处理逻辑
    http.authorizeRequests()
    .anyRequest().authenticated()
    .and()
    .formLogin()
    .loginPage("/myLogin.html")
    //.permitAll() //使登录页不设限访问
    .loginProcessingUrl("/login") //指定处理登录请求的路径
    .successHandler((httpServletRequest, httpServletResponse, authentication) -> {
    httpServletResponse.setContentType("application/json;charset=UTF-8");
    PrintWriter out = httpServletResponse.getWriter();
    out.write("{\"error_code\":\"0\", \"message\":\"欢迎登录系统\"}");
    })
    .failureHandler((httpServletRequest, httpServletResponse, e) -> {
    httpServletResponse.setContentType("application/json;charset=UTF-8");
    PrintWriter out = httpServletResponse.getWriter();
    //输出失败原因
    out.write("{\"error_code\":\"401\", \"name\":\"" + e.getMessage() + "\", \"message\":\"" + e.getMessage() + "\"}");
    })
    .and()
    .csrf()
    .disable();
    }
    }

    认证与授权

    默认数据库模型的认证与授权

    资源准备

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.louris.springboot.controller;

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @RequestMapping("/admin/api")
    public class AdminController {

    @GetMapping("hello")
    public String hello(){
    return "hello, admin";
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.louris.springboot.controller;

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @RequestMapping("/app/api")
    public class AppController {

    @GetMapping("hello")
    public String hello(){
    return "hello, app";
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.louris.springboot.controller;

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @RequestMapping("/user/api")
    public class UserController {

    @GetMapping("hello")
    public String hello(){
    return "hello, user";
    }
    }

    资源授权的配置

  •