springboot 整合 Spring Security 上篇

1.创建springBoot 项目工程(spring6.0的底层、JDK17)

1.添加依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

配置完成启动访问controller会出现登录页面
springboot 整合 Spring Security 上篇_第1张图片

默认用户user 密码 控制台uuid输出,跟踪源码可以看到
springboot 整合 Spring Security 上篇_第2张图片

2 使用配置的用户名密码;
  security:
    user:
      name: admin
      password: 123456

使用自己配置的登录然后就可以访问控制层api

3.退出登录

http://127.0.0.1:8080/logout
springboot 整合 Spring Security 上篇_第3张图片

2.基于内存的多用户管理

springboot 整合 Spring Security 上篇_第4张图片

1.创建用户详情接口配置类


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;
    }


}

访问接口登录 test test 报错:如下
在这里插入图片描述
必须加密
修改配置类

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();
    }


}

通过上面配置可以正常登录,访问api接口,配置文件中的用户失效

3.密码加密

1.CSDN密码泄露事件,数据库明文存储
1.前端密码加密给后端,使用相同的密码加密规则,前端加密对比数据库密码校验规则。

常见加密算法MD5 sha RSA

2、使用PasswordEncoder接口提供的实现类

加盐干扰因子
BCryptPasswordEncoder 加密和校验原始密码

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);

springboot 整合 Spring Security 上篇_第5张图片
加密上面文明密码


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();

    }


}

在这里插入代码片

返回登录信息测试
添加控制器

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;
    }
}

访问接口返回登录用户的信息如下
springboot 整合 Spring Security 上篇_第6张图片
配置权限

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.基于方法的授权

开启@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/**");
    }


}


控制器配置

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.处理结果返回JSON 前端好处理

响应报文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.认证成功处理
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();



    }
}

springboot 整合 Spring Security 上篇_第7张图片

2.认证失败处理器
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();
    }
}

springboot 整合 Spring Security 上篇_第8张图片

3.退出登录处理器
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();
    }
}

springboot 整合 Spring Security 上篇_第9张图片

4.资源访问拒绝处理器
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();
    }
}

springboot 整合 Spring Security 上篇_第10张图片

5.配置各种处理器
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/**");
    }


}


你可能感兴趣的:(java,框架修炼,spring,spring,boot,后端)