springboot integrates Spring Security Part 1

1. Create a springBoot project (the bottom layer of spring6.0, JDK17)

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

After the configuration is completed and the access controller is started, the login page will appear.
Insert image description here

The default user user password console uuid output, you can see the trace source code
Insert image description here

2 Use the configured username and password;
  security:
    user:
      name: admin
      password: 123456

Use your own configured login and then you can access the control layer API

3. Log out

http://127.0.0.1:8080/logout
Insert image description here

2. Memory-based multi-user management

Insert image description here

1. Create user details interface configuration class


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

//自定义配置类实现用户详情
@Configuration
public class MyUserSecurityConfig {
    
    

    @Bean
    public UserDetailsService userDetailsService() {
    
    
        UserDetails user1 = User.builder().username("admin").password("123456").build();
        UserDetails user2 = User.builder().username("test").password("123456").build();
        InMemoryUserDetailsManager memoryUserDetailsManager = new InMemoryUserDetailsManager();
        memoryUserDetailsManager.createUser(user1);
        memoryUserDetailsManager.createUser(user2);

        return memoryUserDetailsManager;
    }


}

Access interface login test test error: The following
Insert image description here
must be encrypted
Modify the configuration class

package com.example.db.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

//自定义类实现用户详情接口
@Configuration
public class MyUserSecurityConfig {
    
    

    @Bean
    public UserDetailsService userDetailsService() {
    
    
        UserDetails user1 = User.builder().username("admin").password("123456")
                .roles("admin").build();
        UserDetails user2 = User.builder().username("test").password("123456")
                .roles("stu").build();
        InMemoryUserDetailsManager memoryUserDetailsManager = new InMemoryUserDetailsManager();
        memoryUserDetailsManager.createUser(user1);
        memoryUserDetailsManager.createUser(user2);

        return memoryUserDetailsManager;
    }

    //配置密码加密器
    //没有加密NoOpPasswordEncoder
    @Bean
    public PasswordEncoder passwordEncoder() {
    
    
        return NoOpPasswordEncoder.getInstance();
    }


}

Through the above configuration, you can log in normally and access the API interface. The user in the configuration file is invalid.

3. Password encryption

1. CSDN password leakage incident, database plain text storage
1. The front-end password is encrypted to the back-end, using the same password encryption rules. The front-end encryption is compared with the database password verification rules.

Common encryption algorithm MD5 sha RSA

2. Use the implementation class provided by the PasswordEncoder interface

Salt interference factor
BCryptPasswordEncoder encrypts and verifies the original password

BCryptPasswordEncoder cryptPasswordEncoder=new BCryptPasswordEncoder();
        String encode = cryptPasswordEncoder.encode("123456");

        String encode2 = cryptPasswordEncoder.encode("123456");
        String encode3 = cryptPasswordEncoder.encode("123456");
        log.info(encode);
        log.info(encode2);
        log.info(encode3);
        boolean pwd1 = cryptPasswordEncoder.matches("123456", encode);
        boolean pwd2 = cryptPasswordEncoder.matches("123456", encode2);
        boolean pwd3 = cryptPasswordEncoder.matches("123456", encode3);
        log.info("pwd1="+pwd1);
        log.info("pwd2="+pwd2);
        log.info("pwd3="+pwd3);

Insert image description here
Encrypt the above civilized password


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

//自定义类实现用户详情接口
@Configuration
public class MyUserSecurityConfig {

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user1 = User.builder().username("admin").password(passwordEncoder().encode("123456"))
                .roles("admin").build();
        UserDetails user2 = User.builder().username("test").password(passwordEncoder().encode("654321"))
                .roles("stu").build();
        InMemoryUserDetailsManager memoryUserDetailsManager = new InMemoryUserDetailsManager();
        memoryUserDetailsManager.createUser(user1);
        memoryUserDetailsManager.createUser(user2);

        return memoryUserDetailsManager;
    }

    //配置密码加密器
    //没有加密NoOpPasswordEncoder
    @Bean
    public PasswordEncoder passwordEncoder() {
//        return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();

    }


}

在这里插入代码片

Return login information test
Add controller

package com.example.db.control;

import org.springframework.boot.autoconfigure.neo4j.Neo4jProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;

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

    @GetMapping("/loginUser")
    public Authentication loginUser(Authentication authentication) {
    
    
        return authentication;
    }

    @GetMapping("/loginUser2")
    public Principal loginUser2(Principal principal) {
    
    
        return principal;
    }

    @GetMapping("/loginUser3")
    public Principal loginUser3() {
    
    
        //通过安全上下文持有器
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication;
    }
}

The access interface returns the logged in user information as follows:
Insert image description here
Configure permissions

UserDetails user1 = User.builder().username("admin").password(passwordEncoder().encode("123456"))
                .roles("admin")
                .authorities("stu:del") //配置权限
{
    
    
	"authorities": [{
    
    
		"authority": "stu:del"
	}],
	"details": {
    
    
		"remoteAddress": "127.0.0.1",
		"sessionId": "026ED34E46FC9022C29B200090F14924"
	},
	"authenticated": true,
	"principal": {
    
    
		"password": null,
		"username": "admin",
		"authorities": [{
    
    
			"authority": "stu:del"
		}],
		"accountNonExpired": true,
		"accountNonLocked": true,
		"credentialsNonExpired": true,
		"enabled": true
	},
	"credentials": null,
	"name": "admin"
}
4. Method-based authorization

开启@EnableGlobalMethodSecurity(prePostEnabled = true)

package com.example.db.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

//从 Spring Security 5.7.0-M2开始 WebSecurityConfigurerAdapter 被标记为过期,鼓励用户转向基于组件的 security 配置
@Configuration
@Slf4j
//全局方法授权

@EnableWebSecurity  // 启用SpringSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
    
    

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    
    


        http.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
                .anyRequest().authenticated());

        http.formLogin().permitAll();
        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
    
    
        return (web) -> web.ignoring().requestMatchers("/test/**");
    }


}


Controller configuration

package com.example.db.control;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/student")
@RestController
public class StudentController {
    
    

    @GetMapping("/stu")
    @PreAuthorize("hasAuthority('stu')")
    public String test() {
    
    
        return "I am a student";
    }
}

5. The processing results are returned to JSON, which is easy to process on the front end.

Response message VO

package com.example.db.pojo;

import lombok.Data;

/**
 * 自定义响应结构
 */
@Data
public class Result {
    
    

    // 响应业务状态
    private Integer code;

    // 响应消息
    private String message;

    // 响应中的数据
    private Object data;

    public Result() {
    
    
    }

    public Result(Object data) {
    
    
        this.code = 200;
        this.message = "OK";
        this.data = data;
    }

    public Result(String message, Object data) {
    
    
        this.code = 200;
        this.message = message;
        this.data = data;
    }

    public Result(Integer code, String message, Object data) {
    
    
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static Result ok() {
    
    
        return new Result(null);
    }

    public static Result ok(String message) {
    
    
        return new Result(message, null);
    }

    public static Result ok(Object data) {
    
    
        return new Result(data);
    }

    public static Result ok(String message, Object data) {
    
    
        return new Result(message, data);
    }

    public static Result build(Integer code, String message) {
    
    
        return new Result(code, message, null);
    }

    public static Result build(Integer code, String message, Object data) {
    
    
        return new Result(code, message, data);
    }


}

1. Authentication successfully processed
package com.example.db.config;

import com.example.db.pojo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

//认证成功处理器
@Component
@Slf4j
public class AuthorSuccesssHandler implements AuthenticationSuccessHandler {
    
    


    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    
    
        Result result=Result.build(1,"登录成功");

        String responsejson=objectMapper.writeValueAsString(result);
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(responsejson);
        response.getWriter().flush();



    }
}

Insert image description here

2.Authentication failure handler
package com.example.db.config;

import com.example.db.pojo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;
//认证失败处理器
@Component

public class AuthorFailHandler implements AuthenticationFailureHandler {
    
    

    @Autowired
   private ObjectMapper objectMapper;
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
    
    
        Result result=Result.build(-1,"登陆失败");

        String responsejson=objectMapper.writeValueAsString(result);
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(responsejson);
        response.getWriter().flush();
    }
}

Insert image description here

3. Exit the login handler
package com.example.db.config;

import com.example.db.pojo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

//注销处理器
@Component
@Slf4j
public class AppLoginOutHandler implements LogoutSuccessHandler {
    
    
    @Autowired
    private ObjectMapper objectMapper;


    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    
    
        Result result = Result.build(1, "退出登录成功");

        String responsejson = objectMapper.writeValueAsString(result);
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(responsejson);
        response.getWriter().flush();
    }
}

Insert image description here

4. Resource access denial handler
package com.example.db.config;

import com.example.db.pojo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

//资源访问拒绝处理器
@Slf4j
@Component
public class AppAcessDeiedHandler implements AccessDeniedHandler {
    
    
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
    
    
        Result result = Result.build(0, "你没有权限访问资源");

        String responsejson = objectMapper.writeValueAsString(result);
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(responsejson);
        response.getWriter().flush();
    }
}

Insert image description here

5. Configure various processors
package com.example.db.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;

//从 Spring Security 5.7.0-M2开始 WebSecurityConfigurerAdapter 被标记为过期,鼓励用户转向基于组件的 security 配置
@Configuration
@Slf4j
//全局方法授权

@EnableWebSecurity  // 启用SpringSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
    
    

    //认证成功处理器
    @Autowired
    private AuthorSuccesssHandler authorSuccesssHandler;
    @Autowired
    //认证失败处理器
    private AuthorFailHandler authorFailHandler;

    //退出登录处理器

    @Autowired
    private AppLoginOutHandler appLoginOutHandler;

    //访问拒绝处理器
    @Autowired
    private AppAcessDeiedHandler appAcessDeiedHandler;


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    
    


        http.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
                .anyRequest().authenticated());

        http.formLogin()
                .successHandler(authorSuccesssHandler)//认证成功
                .failureHandler(authorFailHandler)//认证失败


                .permitAll();

        http.logout().logoutSuccessHandler(appLoginOutHandler); //退出登录
        http.exceptionHandling().accessDeniedHandler(appAcessDeiedHandler);//访问资源失败

        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
    
    
        return (web) -> web.ignoring().requestMatchers("/test/**");
    }


}


Guess you like

Origin blog.csdn.net/qq_30519365/article/details/134753702