background

Login authentication is a necessary function for almost all Internet applications. Traditional user-name and password authentication is still popular. How to prevent sensitive information such as user names and passwords from being sniffed and cracked during authentication?The traditional user name and password are transmitted in plain textRSAEven if the authentication request is captured by the network, as long as the private key is secure, the user information in the authentication process is relatively secure.

  1. It’s usually generatedRSAThe public key is stored in the front end or back end (the back end is requested to return the public key each time during login) for encryption, and the private key is stored in the back end for decryption.
  2. I have seen the practice of dynamically generating the key pair in the actual application, that is, the public key-private key is dynamically generated, and each request is different. Compared with the fixed public key-private key, the performance loss is larger, and the security benefit does not increase much. Therefore, the fixed key pair is used here to demonstrate.

Generates an RSA key pair

There are three commands:

Generate an RSA private key
genrsa -out rsa_private_key.pem 1024

Convert RSA private key to PKCS8 format
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt

# Generate an RSA public key
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
Copy the code
  • Windows operating system: Win10

Download and install OpenSSL: slproweb.com/products/Wi…

D: Program Files\ openSSL-win64 \bin: openssl-win64 \bin: D: Program Files\ openssl-win64 \bin: D: Program Files\ openssl-win64 \bin

OpenSSL> genrsa -out rsa_private_key.pem 1024 Generating RSA private key, 1024 bit long modulus (2 primes) ..................................... + + + + +... +++++ e is 65537 (0x010001) OpenSSL> pkcs8 -topk8 -inform PEM -inrsa_private_key.pem -outform PEM -nocrypt -----BEGIN PRIVATE KEY----- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL/KFpxZ2ZJq4/f8 1oM2LX/aX1llPL6SlFbk5pBw1ESuQDVrcA8T4grdrFoEY6T2mNQAMiuzRKfYkS1l Qx1C+L0HruqOPhFwDL7rxrDQU+8g/trCv+DQoMAbIcteqgxLQrvMZs1OuJrK0XpG p4Ca7Wxfuk8HUynjQ9fhXIjWzWTjAgMBAAECgYBMUAARNFszPF77RNqiGQOftOdt ra+u8KofrTLk1FBSB7e6ycYr6bBuvGeg5dA0Sn7jFDTiWJF/69dQZdN/qC9Kb0OV jRtXDCSMHe1oRlvDr8tZKn9h9UljJHXrIapXJi5Z1eNQ3DW8ltgJbx/DpQrsSTYJ JiWWpwfb6e+ub09JEQJBAOt+DAxec2h1Gq43Fc/fJ6hUmVl0VI0d5WkeVHezhutE gYj29gkHkQin5VIMbXtutB/083vUm+Fxqc5EXdxzYIsCQQDQfb+gNZgBzeNhF/j5 IdqW68PpSOmWj2z9sVvAktSS9VzTt46haBvnjzIbES+uzJXoW0LI0H1zDlbvbtRV HQAJAkEAz+kQMBdvowjIzok5y7ZEqBxQ66aGQ7TiZ2Vsw+YPt0VbbBZF8IDqro61 KzRnsLNzekdkdK6oFWmptr+rcse2swJARN10QSfSqK3n7/cqHqgm+nivgku6FCgV uQovI0Gcg1oWKjxUGU45AVhUFYqstFERJumV+pybAzj2UCnMarykeQJAAkXb5Z7A sb7wmLCDMoyfzJCn54k1VDEvGVcrn4SiME53wEyGnrYkyg8R84hO7rHLOnwz0PtZ iLWuHpqd2OovmA== -----END PRIVATE KEY----- OpenSSL> rsa  -in rsa_private_key.pem -pubout -out rsa_public_key.pem
writing RSA key
Copy the code
  • Linux: CentOS7

Again, execute the above three linesRSAKey pair generation:Note:

  1. In the subsequent coding implementation, the secret key generated on Windows is used to demonstrate;
  2. The public and private keys are marked by the red ellipse in the following figure.

Public key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/yhacWdmSauP3/NaDNi1/2l9ZZTy+kpRW5OaQcNRErkA1a3APE+IK3axaBGOk9pjUADIrs0Sn2JEtZUMd Qvi9B67qjj4RcAy + 68 aw0fpvip7awr/g0KDAGyHLXqoMS0K7zGbNTriaytF6RqeAmu1sX7pPB1Mp40PX4VyI1s1k4wIDAQAB private key: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL/KFpxZ2ZJq4/f81oM2LX/aX1llPL6SlFbk5pBw1ESuQDVrcA8T4grdrFoEY6T2mNQAMiuz RKfYkS1lQx1C+L0HruqOPhFwDL7rxrDQU+8g/trCv+DQoMAbIcteqgxLQrvMZs1OuJrK0XpGp4Ca7Wxfuk8HUynjQ9fhXIjWzWTjAgMBAAECgYBMUAARNFsz PF77RNqiGQOftOdtra+u8KofrTLk1FBSB7e6ycYr6bBuvGeg5dA0Sn7jFDTiWJF/69dQZdN/qC9Kb0OVjRtXDCSMHe1oRlvDr8tZKn9h9UljJHXrIapXJi5Z 1eNQ3DW8ltgJbx/DpQrsSTYJJiWWpwfb6e+ub09JEQJBAOt+DAxec2h1Gq43Fc/fJ6hUmVl0VI0d5WkeVHezhutEgYj29gkHkQin5VIMbXtutB/083vUm+Fx qc5EXdxzYIsCQQDQfb+gNZgBzeNhF/j5IdqW68PpSOmWj2z9sVvAktSS9VzTt46haBvnjzIbES+uzJXoW0LI0H1zDlbvbtRVHQAJAkEAz+kQMBdvowjIzok5 y7ZEqBxQ66aGQ7TiZ2Vsw+YPt0VbbBZF8IDqro61KzRnsLNzekdkdK6oFWmptr+rcse2swJARN10QSfSqK3n7/cqHqgm+nivgku6FCgVuQovI0Gcg1oWKjxU GU45AVhUFYqstFERJumV+pybAzj2UCnMarykeQJAAkXb5Z7Asb7wmLCDMoyfzJCn54k1VDEvGVcrn4SiME53wEyGnrYkyg8R84hO7rHLOnwz0PtZiLWuHpqd 2OovmA==Copy the code

The back-end service

Based on SpringBoot, SpringSecurity implements user authentication.

Project depend on

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

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

SpringSecurity configuration

Pay attention to the discharge authentication interface. Otherwise, 403 is displayed.

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder(a) {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/auth/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // turn off csrf, or will be 403 forbidden.csrf().disable(); }}Copy the code

User Information Configuration

To focus on this article’s user-name-password encrypted transmission and avoid introducing additional complexity, we use in-memory user information to demonstrate this. For information about retrieving user information from a database, see 6-SpringSecurity: Database Storage of User Information.

@Component
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return User.withUsername("dev").password(new BCryptPasswordEncoder().encode("123")).authorities("p1"."p2").build(); }}Copy the code

Authentication interface

Here the private key is configured in applicaton.yml.

@RestController
@RequestMapping("auth")
@Slf4j
public class LoginController {
    @Value("${rsa.private_key}")
    private String privateKey;

    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    public LoginController(AuthenticationManagerBuilder authenticationManagerBuilder) {
        this.authenticationManagerBuilder = authenticationManagerBuilder;
    }

    @PostMapping("/login")
    public String login(@RequestBody FormUser formUser, HttpServletRequest request) {
        log.info("formUser encrypted: {}", formUser);

        // Decrypt user information RSA private key. Method 1: Customize tool class: RSAEncrypt
// String username = RSAEncrypt.decrypt(formUser.getUsername(), privateKey);
// String password = RSAEncrypt.decrypt(formUser.getPassword(), privateKey);
// log.info("Userinfo decrypted: {}, {}", username, password);

        // Decrypt user information RSA private key. Method 2: Use the tool class in Hutool to decrypt user information
        RSA rsa = new RSA(privateKey, null);
        String username = new String(rsa.decrypt(formUser.getUsername(), KeyType.PrivateKey));
        String password = new String(rsa.decrypt(formUser.getPassword(), KeyType.PrivateKey));
        log.info("Userinfo decrypted: {}, {}", username, password);

        // Verify the username and password
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        log.info("authentication: {}", authentication);

        returnSecurityContextHolder.getContext().getAuthentication().getPrincipal().toString(); }}Copy the code

Custom tool class for decryption

<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.12</version>
</dependency>
Copy the code
public class RSAEncrypt {
    /** * RSA public key encryption *@paramSTR Character string * to be encrypted@paramPublicKey public key *@returnCipher * /
    public static String encrypt(String str, String publicKey) {
        try {
            // Base64 encoded public key
            byte[] decoded = Base64.decodeBase64(publicKey);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            / / RSA encryption
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            return Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
        } catch (Exception e) {
            throw newRuntimeException(e); }}/** * RSA private key decryption *@paramSTR is an encrypted string *@paramPrivateKey private key *@returnClear * /
    public static String decrypt(String str, String privateKey) {
        try {
            // 64-bit decoded encrypted string
            byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
            // Base64 encoded private key
            byte[] decoded = Base64.decodeBase64(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            / / RSA decryption
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            return new String(cipher.doFinal(inputByte));
        } catch (Exception e) {
            throw newRuntimeException(e); }}}Copy the code

Decrypt using the utility classes in Hutool

<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.0.6</version>
</dependency>	
Copy the code

The front-end engineering

Based on Vue3.0, AXIOS implements a minimalist login page.

Note:

  1. The premise needs to beNode.jsEnvironment, available for usenvmforNode.jsMulti-version management; May refer toHeartsuit.blog.csdn.net/article/det…
  2. npm install <package>By default, dependencies are written after installation is completepackage.json, so the commands that the installation depends on are not attachedsaveParameters.
V12.16.1 $node - vCopy the code

Install vuE-CLI and create the project

npm install -g @vue/cli
vue --version
vue create hello-world
Copy the code

The initial package.json dependency looks like this:

  "dependencies": {
    "core-js": "^ 3.6.5." "."vue": "^ 3.0.0"
  },
Copy the code

Integrated Axios

  • Install dependencies
npm install axios
Copy the code

The package.json dependency becomes:

  "dependencies": {
    "axios": "^ 0.21.1"."core-js": "^ 3.6.5." "."vue": "^ 3.0.0"
  },
Copy the code
  • According to the need to introduce

Import axios from “axios” in components that need to use axios;

Integrated jsencrypt

The package.json dependency becomes:

  "dependencies": {
    "axios": "^ 0.21.1"."core-js": "^ 3.6.5." "."jsencrypt": "^ 3.2.1." "."vue": "^ 3.0.0"
  },
Copy the code
  • According to the need to introduce

Import JSEncrypt from “JSEncrypt” in components that need to use JSEncrypt;

The final front-end login component code

<template> <div> <span> <span> <input type="text" V-model ="user.username" /> <span> Password </span><input type="text" /> <input type="submit" V-on :click="login" value=" login" /> </div> </template> <script> import { defineComponent } from "vue"; import axios from "axios"; import JSEncrypt from "jsencrypt"; export default defineComponent({ name: "RSADemo", setup() {}, data() { return { user: { username: "dev", password: 123 }, publicKey: `MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/yhacWdmSauP3/NaDNi1/2l9Z ZTy+kpRW5OaQcNRErkA1a3APE+IK3axaBGOk9pjUADIrs0Sn2JEtZUMdQvi9B67q jj4RcAy+68aw0FPvIP7awr/g0KDAGyHLXqoMS0K7zGbNTriaytF6RqeAmu1sX7pP B1Mp40PX4VyI1s1k4wIDAQAB`, }; }, mounted() { this.login(); }, methods: { login: function () { let userinfo = { username: this.encrypt(this.user.username), password: this.encrypt(this.user.password), }; axios.post("http://localhost:8000/auth/login", userinfo).then( function (res) { if (res.status == 200) { console.log(res.data); } else { console.error(res); } }, function (res) { console.error(res); }); }, encrypt: function (str) { let jsEncrypt = new JSEncrypt(); Jsencrypt.setpublickey (this.publickey); // set the encrypted publicKey, which is usually obtained through the back-end interface. let encrypted = jsEncrypt.encrypt(str.toString()); return encrypted; ,}}}); </script>Copy the code

RSA encryption transmission effect

Possible problems

  • Development environments cross domains

Method 1: Forward requests through the proxy service of the development environment (production environment can be implemented through Nginx), and create a vue.config.js file with the following contents:

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: 'http://localhost:8000/'.changeOrigin: true.ws: true.secure: true.pathRewrite: {
                    '^/api': ' '}}}}};Copy the code

Method 2: Since the backend service is developed by ourselves, we can configure CORS on the backend to allow cross-domain

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Bean
    public WebMvcConfigurer corsConfigurer(a) {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/ * *").
                        allowedOriginPatterns("*").
                        allowedMethods("*").
                        allowedHeaders("*").
                        allowCredentials(true).
                        exposedHeaders(HttpHeaders.SET_COOKIE).maxAge(3600L); }}; }}Copy the code

Attached: code to generate AN RSA key pair

Of course, in addition to using openSSL tools on Windows and Linux to generate key pairs, we can also use code to generate key pairs directly.

<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcprov-jdk15on</artifactId>
  <version>1.64</version>
</dependency>
Copy the code
public class RSAEncrypt {
    private static final KeyPair keyPair = genKeyPair() ;
    private static org.bouncycastle.jce.provider.BouncyCastleProvider bouncyCastleProvider = null;

    public static synchronized org.bouncycastle.jce.provider.BouncyCastleProvider getInstance(a) {
        if (bouncyCastleProvider == null) {
            bouncyCastleProvider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
        }
        return bouncyCastleProvider;
    }

    /** * Randomly generates a key pair */
    public static KeyPair  genKeyPair(a)  {
        try {
// Provider provider =new org.bouncycastle.jce.provider.BouncyCastleProvider();
// Security.addProvider(DEFAULT_PROVIDER);
            SecureRandom random = new SecureRandom();
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", getInstance());
            generator.initialize(1024,random);
            return generator.generateKeyPair();
        } catch(Exception e) {
            throw newRuntimeException(e); }}/** * Obtain the public key string (base64 string) *@return* /
    public static String generateBase64PublicKey(a) {
        PublicKey  publicKey = (RSAPublicKey) keyPair.getPublic();
        return new String(Base64.encodeBase64(publicKey.getEncoded()));
    }
    /** * Obtain the private key string (base64 string) *@return* /
    public static String generateBase64PrivateKey(a) {
        PrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // Get the private key string
        return newString(Base64.encodeBase64((privateKey.getEncoded()))); }... }Copy the code

Reference

  • Source Code: Github
  • Blog.csdn.net/aexlinda/ar…

If you have any questions or any bugs are found, please feel free to contact me.

Your comments and suggestions are welcome!

This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.