spring security6重写登陆验证

news2024/10/6 17:26:20
  1. spring security的认证流程
    在这里插入图片描述

在这里插入图片描述
2. 从文档上可以看出来,UsernamePasswordAuthenticationFilter和AuthenticationManager是认证的关键步骤,/login传过来的username和password由UsernamePasswordAuthenticationFilter接收处理成UsernamePasswordAuthenticationToken,再传给AuthenticationManager,在AuthenticationManager中判断密码正确与否
3. 代码实现
(一)、登录部分:登录可以使用两种方式进行登陆
第一种:只用spring security默认提供的/login接口进行登录(这种方式需要修改spring security默认的登录设置,比较办法)
第二种:自己写登陆的controller(这种比较简单就不研究了)

第一步 WebSecurityConfig配置文件

package com.xuhao.springsecuritycom.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xuhao.springsecuritycom.filters.CustomUsernamePasswordAuthenticationFilter;
import com.xuhao.springsecuritycom.filters.TokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableWebSecurity
// 开启注解模式,否则权限注解会无效
@EnableGlobalMethodSecurity(
        prePostEnabled = true,  // 启用@PreAuthorize和@PostAuthorize注解
        securedEnabled = true   // 启用@Secured注解)
)
public class SecurityConfig {
    @Autowired
    private TokenFilter tokenFilter;
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .authorizeHttpRequests((requests) -> requests
                        .requestMatchers(HttpMethod.POST,"/user/create", "/login").permitAll()
                        .anyRequest().authenticated()
                )
                .logout((logout) -> logout.permitAll());
        // 设置无权访问时的返回
        httpSecurity.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler());
        // 设置未登录,访问受保护地址的拦截
        httpSecurity.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint());
        // 配置token拦截过滤器,这个过滤器很重要,用来判断用户是否登录的逻辑
        httpSecurity.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 关闭csrf,否则ajax无法通过拦截
        httpSecurity.csrf().disable();
        // 不通过Session获取SecurityContext
        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        return httpSecurity.build();
    }
    /**
     * 设置加密规则
     * PasswordEncoderFactories.createDelegatingPasswordEncoder()
     * 创建的DelegatingPasswordEncoder会自动根据配置的密码存储类型来选择合适的加密规则
     * */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
    /**
     * 这是登录过滤器的bean
     */
    @Bean
    public CustomUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() {
        CustomUsernamePasswordAuthenticationFilter loginFilter = new CustomUsernamePasswordAuthenticationFilter();
        loginFilter.setAuthenticationManager(customAuthenticationManager());
        return loginFilter;
    }

    // 设置判断登录成功的实现类的bean
    @Bean
    public CustomAuthenticationManager customAuthenticationManager() {
        return new CustomAuthenticationManager();
    }

}


第二步 重写登录接口/login的逻辑

package com.xuhao.springsecuritycom.filters;

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 org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 重写登录拦截过滤器
 * */
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    // 接收请求参数
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        String username = this.obtainUsername(request);
        username = username != null ? username.trim() : "";
        String password = this.obtainPassword(request);
        password = password != null ? password : "";
        UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
        this.setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }
    // 设置/login接口登录成功以后得接口返回
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","登陆成功");
        // 获取用户信息这里也可以把生成的token返回给前端,写死token懒得接jwt了,后面就判断token=1就是登录了
        map.put("token","1");
        map.put("authentication",authResult);
        String string = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(string);
    }
    // 设置/login接口登录失败以后得接口返回
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        Map<String,Object> map = new HashMap<>();
        map.put("code",302);
        map.put("msg","登陆失败");
        String string = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(string);
    }
}


第三步 重写AuthenticationManager,用于判断密码是否正确

package com.xuhao.springsecuritycom.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.ArrayList;
import java.util.Collection;

public class CustomAuthenticationManager  implements AuthenticationManager {
    @Autowired
    PasswordEncoder passwordEncoder;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 从传入的 Authentication 对象中获取用户提供的凭证信息,比如用户名和密码
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
        // 写死密码123456用户名xuhao,开发时应该是从数据库获取到的密码
        String contrastPaw = passwordEncoder.encode("123456");
        // 模拟一个权限列表
        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_ADD"));

        if(username.equals("xuhao") && passwordEncoder.matches(password, contrastPaw)) {
            return new UsernamePasswordAuthenticationToken(username, password, authorities);
        } else {
            throw new BadCredentialsException("Invalid username or password");
        }
    }
}


第四步 重写未登录,未授权的异常返回

package com.xuhao.springsecuritycom.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
 * 未授权访问拦截
 * */
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        Map<String,Object> map = new HashMap<>();
        map.put("code",403);
        map.put("msg","您无权访问此地址");
        String string = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(string);
    }
}
package com.xuhao.springsecuritycom.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 未登录返回拦截
 * */
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        Map<String,Object> map = new HashMap<>();
        map.put("code",401);
        map.put("msg","请登录后访问");
        String string = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(string);
    }
}

第五步 写测试接口

package com.xuhao.springsecuritycom.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/user")
public class User {
    @Autowired
    PasswordEncoder encoder;
    @PostMapping("/create")
    public ResponseEntity<?> createUser(@RequestParam String userName, @RequestParam String password) {
        Map<String, String> map = new HashMap<>();
        map.put("userName", userName);
        map.put("password", encoder.encode(password));
        System.out.println(encoder.matches("123456", encoder.encode(password)));
        return ResponseEntity.ok().body(map);
    }


    @GetMapping("/see")
    // 测试测试权限,全局写死权限为ROLE_ADD,所以访问SEE时就会被权限过滤器拦截
    @Secured("ROLE_SEE")
    public ResponseEntity<?> seeUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return ResponseEntity.ok().body(authentication);
    }
}

附上源码地址: spring security6示例代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1571036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式总结-组合模式

组合设计模式 模式动机模式定义模式结构组合模式实例与解析实例一&#xff1a;水果盘实例二&#xff1a;文件浏览 更复杂的组合总结 模式动机 对于树形结构&#xff0c;当容器对象&#xff08;如文件夹&#xff09;的某一个方法被调用时&#xff0c;将遍历整个树形结构&#x…

基于SSM的品牌银饰售卖平台(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的品牌银饰售卖平台&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring …

LeetCode - 边积分最高的节点

2374. 边积分最高的节点 这是一个有向图&#xff0c;且每个节点都只有一条出边&#xff0c;指向0的边有1&#xff0c;2&#xff0c;3&#xff0c;4 10&#xff0c; 指向7的有5&#xff0c;6 11. 我们只需要一次遍历就可以解决&#xff0c;先搞一张哈希表&#xff0c;k存节点…

docker安装nacos,单例模式(standalone),使用内置的derby数据库,简易安装

文章目录 前言安装创建文件夹docker指令安装docker指令安装-瘦身版 制作docker-compose.yaml文件查看页面 前言 nacos作为主流的服务发现中心和配置中心&#xff0c;广泛应用于springcloud框架中&#xff0c;现在就让我们一起简易的部署一个单例模式的nacos&#xff0c;版本可…

Qt+OpenGL-part3

1-4EBO画矩形_哔哩哔哩_bilibili 可以绘制两个三角形来组成一个矩形&#xff08;OpenGL主要处理三角形&#xff09; 直接画两个三角形&#xff1a; #include "openglwidget.h" #include <QDebug>unsigned int VBO,VAO; unsigned int shaderProgram;//顶点着…

快排序解读

排序算法是计算机科学中不可或缺的一部分&#xff0c;它们在各种数据处理场景中发挥着关键作用。在众多排序算法中&#xff0c;快速排序以其高效的性能和简洁的实现成为了许多程序员的首选。今天&#xff0c;我们就来深入剖析快速排序算法&#xff0c;了解其原理、实现方式以及…

【【萌新的Pytorch入门之Python的学习】】

学习记录 - 参考记录来自B站up主 -爆肝杰哥 ① NumPy 包为 Python 加上了关键的数组变量类型&#xff0c;弥补了 Python 的不足&#xff1b; ② Pandas 包在 NumPy 数组的基础上添加了与 Excel 类似的行列标签&#xff1b; ③ Matplotlib 库借鉴 Matlab&#xff0c;帮 Python 具…

mysql 基本查询

学习了mysql函数&#xff0c;接下来学习mysql基本查询。 1&#xff0c;基本查询语句 MySQL从数据表中查询数据的基本语句为SELECT 语句。SELECT语句的基本格式是&#xff1a; SELECT (*I <字段列表>} FROM <表1>,<表2>..[WHERE<表达式> [GROUP BY <…

C++入门(以c为基础)——学习笔记2

1.引用 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空 间。在语法层面&#xff0c;我们认为它和它引用的变量共用同一块内存空间。 可以取多个别名&#xff0c;也可以给别名取别名。 b/c/d本质都是别名&#…

QA测试开发工程师面试题满分问答3: python的深拷贝和浅拷贝问题

在 Python 中&#xff0c;深拷贝&#xff08;deep copy&#xff09;和浅拷贝&#xff08;shallow copy&#xff09;是用于创建对象副本的两种不同方式。 浅拷贝是创建一个新的对象&#xff0c;该对象与原始对象的内容相同&#xff08;包括内部嵌套对象的引用&#xff09;&…

SSM框架学习——了解Spring与Eclipse创建Maven项目

了解Spring 什么是Spring Spirng是分层的JavaSE/EE全栈轻量级开源框架&#xff0c;以控制反转IoC和面向切面编程AOP为内核&#xff0c;使用基本的JavaBean来完成EJB的工作。 Spring框架采用分层架构&#xff0c;它的一些列功能被分为若干个模块。 上图中的红色背景模块为本…

提示工程概要

提示工程 1. 两大原则 原则 1&#xff1a;编写清晰具体的说明 使用分隔符 三引号&#xff1a;“”"三个反引号&#xff1a;三个破折号&#xff1a;—尖括号&#xff1a;<>XML标签&#xff1a; 要求结构化输出 HTMLJSONXMLPython字典 检查条件是否满足 检查执行…

C++ 类和对象(初篇)

类的引入 C语言中&#xff0c;结构体中只能定义变量&#xff0c;在C中&#xff0c;结构体内不仅可以定义变量&#xff0c;也可以定义函数。 而为了区分C和C我们将结构体重新命名成class去定义 类的定义 标准格式&#xff1a; class className {// 类体&#xff1a;由成员函…

linux安装和使用Rancher

linux安装和使用Rancher Rancher介绍请看如下博客 arm架构安装Rancher并导入k8s集群解决Error: no objects passed to apply 华为云arm架构安装k8s(kubernetes) linux下安装Rancher Rancher部署监控k8s集群状态等,比Dashboard插件强大 提前安装好K8S 在master上执行#如果…

学习心得1

这时我第一次更学习心得&#xff01;不足的在评论区指教。 首先先来分享一下&#xff0c;刷一维数组题目的方法。 仔细读题&#xff0c;不会做的题目先完成输入输出。不要干等着着急&#xff0c;就跳到下一题。如果使用的时oj&#xff0c;那就没有题解但是使用洛谷、LeetCood…

【热门话题】计算机视觉入门:探索数字世界中的“视觉智能”

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 计算机视觉入门&#xff1a;探索数字世界中的“视觉智能”摘要正文一、计算机视…

MATLAB——知识点备忘

最近在攻略ADC建模相关方面&#xff0c;由好多零碎的知识点&#xff0c;这里写个备忘录。 Matlab 判断一个数是否为整数 1. isinteger 函数 MATLAB中&#xff0c;可以使用 isinteger 函数来判断一个数是否为整数&#xff0c;例如&#xff1a;要判断x是否为整数可以采用以下代…

链式二叉树经典OJ题目(二)

目录 结构体及头文件&#xff1a; 1.二叉树的前序遍历 题目描述&#xff1a; 思路分析&#xff1a; 源码&#xff1a; 2.二叉树的翻转 题目描述&#xff1a; 思路分析&#xff1a; 源码&#xff1a; 3.另一颗子树 题目描述&#xff1a; 思路分析&#xff1a; 源码&…

蓝桥杯单片机真题实践篇

这里就不完全写思路过程代码什么的&#xff0c;这一篇文章就写我在训练真题中遇到的过程。 &#xff08;呜呜呜&#xff0c;时间不够辣&#xff0c;能做多少算多少吧....&#xff09; 十三届省赛题 问题1&#xff1a;数码管的数字消影不明显 &#xff08;参考&#xff1a;蓝…

Splunk Attack Range:一款针对Splunk安全的模拟测试环境创建工具

关于Splunk Attack Range Splunk Attack Range是一款针对Splunk安全的模拟测试环境创建工具&#xff0c;该工具完全开源&#xff0c;目前由Splunk威胁研究团队负责维护。 该工具能够帮助广大研究人员构建模拟攻击测试所用的本地或云端环境&#xff0c;并将数据转发至Splunk实例…