Spring Boot security

Table of contents

1 Overview

2.token

2.1. Theory

2.2. Use

3.JWT

3.1. Theory

3.2. Use

4.oauth

5.Spring Security

5.1. Overview

5.2. Basic Authentication and Authorization

5.3. Encryption


1 Overview

On the back end, security is mainly to control user access, so that users with corresponding permissions can access corresponding resources, mainly in two points:

  • certified
  • authorized

Authentication, to identify who.     

Authorize, verify permissions.

Every security framework is actually designed to achieve these two points.

Currently commonly used implementation methods are as follows:

  • token
  • JWT
  • oauth
  • spring security

The first three are ideas, and the last is an out-of-the-box framework.

2.token

2.1. Theory

token, also called "token", is a credential for authenticating a user. The composition of the token is arbitrary, as long as it can identify the user's identity.

Token workflow:

 The client sends a request to the server, and the server generates a token and returns it to the client after receiving it. After that, any authentication of the client is based on the token.

2.2. Use

The following is a simple code example of using token to check whether the user is legal in Spring Boot. Of course, in actual work, a lot of content can be placed in the token, such as permissions, which can be authenticated based on the permissions after the backend parses. You can also add relevant information such as a time limit to the token to control the validity period of the token.

token tool class:

 controller:

3.JWT

3.1. Theory

JWT, Json web token, is a common token standard based on json. The token is inherently arbitrary. JWT standardizes the format of the token.

JWT stipulates that token consists of three parts:

  • header

    The header, carrying two pieces of information:

    • Claim type, that is, declare that this is jwt

    • Declare the encryption algorithm, the default is to use the HMAC SHA256 encryption algorithm

  • payload

    Load, where valid information (data information) is stored.

  • signature

    Visa, which can be used to verify the integrity of the entire token and whether it has been tampered with, consists of three parts:

    • header (after base64)

    • payload (after base64)

    • secretprivate key

3.2. Use

JWT is essentially a token with a standard message format. In fact, we can write one by hand if we want. This is not difficult, just define a class. There are also many open source implementations of JWT on the market, just use them directly. The following is a simple demo that uses Spring Boot+JJWT to implement authentication. You don’t need to delve into it, just feel the implementation of others.

Tools:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.function.Function;

@Component
public class JwtUtil {

    // 读取配置文件中的JWT秘钥
    @Value("${jwt.secret}")
    private String secret;

    // JWT的过期时间,单位毫秒
    private final long JWT_EXPIRATION = 1000 * 60 * 60 * 24;

    /**
     * 生成JWT
     * @param username 用户名
     * @return JWT
     */
    public String generateToken(String username) {
        // 设置JWT的过期时间为当前时间 + JWT_EXPIRATION
        Date expiration = new Date(System.currentTimeMillis() + JWT_EXPIRATION);

        // 使用JWT的Builder类构造JWT
        return Jwts.builder()
                .setSubject(username) // 设置JWT的主题为用户名
                .setIssuedAt(new Date()) // 设置JWT的发行时间为当前时间
                .setExpiration(expiration) // 设置JWT的过期时间
                .signWith(SignatureAlgorithm.HS256, secret) // 使用HS256算法和秘钥对JWT进行签名
                .compact(); // 构造JWT并返回
    }

    /**
     * 验证JWT是否有效
     * @param token JWT
     * @return JWT是否有效
     */
    public boolean validateToken(String token) {
        try {
            // 使用JWT的parser类解析JWT
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();

            // 如果JWT的过期时间早于当前时间,则JWT无效
            Date expiration = claims.getExpiration();
            if (expiration.before(new Date())) {
                return false;
            }

            // JWT有效
            return true;
        } catch (Exception e) {
            // 解析JWT失败,则JWT无效
            return false;
        }
    }

    /**
     * 从JWT中获取主题
     * @param token JWT
     * @return 主题
     */
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 从JWT中获取指定claim的值
     * @param token JWT
     * @param claimsResolver 指定claim的解析器
     * @param <T> 指定claim的类型
     * @return 指定claim的值
     */
    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 解析JWT中的所有claim
     * @param token JWT
     * @return 包含所有claim的Claims对象
     */
    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }
}

Controller:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        // TODO: 实现用户登录逻辑
        String username = "exampleUser";
        String token = jwtUtil.generateToken(username);
        return ResponseEntity.ok(new JwtResponse(token));
    }

    @GetMapping("/validateToken")
    public ResponseEntity<?> validateToken(@RequestParam("token") String token) {
        if (jwtUtil.validateToken(token)) {
            return ResponseEntity.ok().build();
        } else {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
    }

    private static class LoginRequest {
        private String username;
        private String password;

        // getters and setters
    }

    private static class JwtResponse {
        private String token;

        public JwtResponse(String token) {
            this.token = token;
        }

        // getter
    }
}

4.oauth

OAUTH, Open Authorization, an open authorization protocol, provides a safe, open and simple standard for authorization of user resources. The purpose is to allow the third party to have limited access to the user's data, but cannot touch the user's core information.

For example, using WeChat or QQ as an account to log in on a third-party website is to use the oauth protocol, which only returns information such as user name and avatar injected to the third party, but does not return core data such as secrets to the third party.

Note: oauth will not be expanded here for the time being, nor will it provide code implementation examples, because the expansion will be relatively long. Here is just a brief mention of this concept. In the next blog post, the blogger will write oauth specifically.

The following is an example of the process of using QQ to do oauth on MOOC. Let’s feel it:

5.Spring Security

5.1. Overview

Spring Security is a security framework based on the Spring framework, which provides a series of security services and the ability to manage application security. The main goal of Spring Security is to protect applications from unauthorized access while supporting common authentication and authorization schemes.

Note: Since this article only introduces some security solutions that can be used in combination with Spring Boot, we are only limited to explaining the basic use of Spring Security. In the next article, the author will use a large space to write Spring Security in detail.

5.2. Basic Authentication and Authorization

rely:

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

Configuration:

Configure a memory-based user repository with two users ("user" and "admin") and specify their passwords using the password encoder "{noop}". The configuration also specifies that the "/admin/**" endpoints require the ADMIN role for access, and that all other endpoints require authentication.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   // 配置内存中的用户存储库
   @Autowired
   public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
      auth.inMemoryAuthentication()
         .withUser("user").password("{noop}password").roles("USER")
         .and()
         .withUser("admin").password("{noop}password").roles("USER", "ADMIN");
         // 创建两个用户: "user"和"admin",使用密码编码器"{noop}"指定密码
   }

   // 配置HTTP请求的安全性
   @Override
   protected void configure(HttpSecurity http) throws Exception {
      http.authorizeRequests()
         .antMatchers("/admin/**").hasRole("ADMIN") // /admin/**端点需要ADMIN角色
         .anyRequest().authenticated() // 其他端点需要进行身份验证
         .and()
         .formLogin() // 启用基于表单的登录
         .and()
         .httpBasic(); // 启用HTTP基本认证
   }
}

In addition to being stored in memory, user roles can also be stored on other storage media, and can be stored in a database if necessary:

Controller:

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

   @GetMapping("/")
   public String adminHome() {
      return "Welcome to the admin page!";
   }
}

5.3. Encryption

Spring Security provides a variety of encryption methods, including the following:

  1. BCryptPasswordEncoder: This is one of the most commonly used encryption algorithms, it uses a hash and a random salt to encrypt passwords.

  2. Pbkdf2PasswordEncoder: This is also a password encryption algorithm that uses a password-based key derivation function (PBKDF2) to encrypt passwords.

  3. SCryptPasswordEncoder: This is a memory-based password hashing algorithm that uses a lot of memory to prevent hash collision attacks.

  4. NoOpPasswordEncoder: This is an insecure encryption algorithm, which only uses plaintext passwords as encrypted passwords. Not recommended for use in production environments.

Encryption with Spring Security typically requires the following steps:

  1. Define the PasswordEncoder bean in the Spring Security configuration.

  2. Use the PasswordEncoder bean to encrypt the user password before saving it to the database.

  3. During the authentication process, Spring Security will compare the password entered by the user with the encrypted password to determine whether the user is authorized to access the resource.

Here is an example of using BCryptPasswordEncoder to encrypt a password:

Spring security provides different Encoders for different supported encryption algorithms.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Bean
   public PasswordEncoder passwordEncoder() {
      return new BCryptPasswordEncoder();
   }

   // ...其他配置
}

Encrypt with encoder:

@Autowired
private PasswordEncoder passwordEncoder;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   auth.inMemoryAuthentication()
      .withUser("user").password(passwordEncoder.encode("password")).roles("USER")
      .and()
      .withUser("admin").password(passwordEncoder.encode("password")).roles("USER", "ADMIN");
}

Guess you like

Origin blog.csdn.net/Joker_ZJN/article/details/130231628