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 textRSA
Even 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.
- It’s usually generated
RSA
The 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. - 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 linesRSA
Key pair generation:Note:
- In the subsequent coding implementation, the secret key generated on Windows is used to demonstrate;
- 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:
- The premise needs to be
Node.js
Environment, available for usenvm
forNode.js
Multi-version management; May refer toHeartsuit.blog.csdn.net/article/det… npm install <package>
By default, dependencies are written after installation is completepackage.json
, so the commands that the installation depends on are not attachedsave
Parameters.
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.