Spring Security 简单token配置

news2024/11/29 3:50:31

Spring Security 简单token配置

说明:非表单配置

先上码: https://gitee.com/qkzztx_admin/security-demo/tree/master/demo-two

环境:win10 idea2023 springboot2.7.6 maven3.8.6

代码清单说明

依赖:

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

Spring Security配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.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.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
    @Resource
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Resource
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Resource
    private AuthFilter authFilter;

    /**
     * security配置
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable() // 禁用csrf
                .authorizeRequests().antMatchers("/login").permitAll() // 允许任何人访问登录接口
                .and()
                .sessionManagement(sessionManager -> sessionManager.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 不再管理session
                .authorizeRequests().anyRequest().authenticated() // 除了所有允许匿名访问的接口外,任何其他接口都得先认证
                .and()
                .exceptionHandling(handling -> {
                    handling.accessDeniedHandler(myAccessDeniedHandler) // 访问被拒绝的处理器
                            .authenticationEntryPoint(myAuthenticationEntryPoint); // 认证失败的处理入口
                });
        // 设置用户访问前filter
        http.addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    /**
     * 用户数据
     */
    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.builder()
                .username("user")
                .password(passwordEncoder().encode("password"))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    /**
     * 密码加密类
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

放开/login的访问,方便用户认证,也就是抛弃了spring security的默认认证接口。
配置中,还有简单的用户查询服务,和一个默认的密码加密Bean。

import com.example.demo.two.controller.vo.R;
import com.example.demo.two.controller.vo.UserRequest;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

@RestController
public class LoginController {
    /**
     * 简单的存放用户登录认证成功信息的地方
     */
    public final static Map<String, UserDetails> TOKEN_USERNAME = new HashMap<>();
    /**
     * SecurityConfig中配置的userDetailsService
     */
    @Resource
    private UserDetailsService userDetailsService;
    /**
     * SecurityConfig中配置的密码加密
     */
    @Resource
    private PasswordEncoder passwordEncoder;
    /**
     * 登录认证,获得token
     * @param userRequest 用户名和密码
     * @return 认证token
     */
    @PostMapping("/login")
    public R login(@RequestBody UserRequest userRequest) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(userRequest.getUsername());
        if (Objects.isNull(userDetails)) {
            return R.fail("用户名不存在");
        }
        if(passwordEncoder.matches(userRequest.getPassword(), userDetails.getPassword())) {
            // 认证成功发个token
            String token = UUID.randomUUID().toString();
            // 认证成功信息,简单存储
            TOKEN_USERNAME.put(token, userDetails);
            return R.success("登录成功", token);
        }
        return R.fail("密码不正确");
    }

    @GetMapping("/")
    public String index() {
        return "首页";
    }
}

简单的认证接口,注入了用户查询服务和密码加密Bean。
查询用户,判断密码,生成token,保存token,返回token,很简单。
还有一个首页接口是需要认证才能访问。

访问资源无权限时的处理类

import com.example.demo.two.controller.vo.R;
import com.example.demo.two.util.SimpleResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Resource
    private SimpleResponseUtil simpleResponseUtil;
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        // 无权限,一般返回403
        log.info("拒绝访问:{}", accessDeniedException.getMessage());
        simpleResponseUtil.write(response, R.fail("拒绝访问"));
    }
}

未登录访问资源时的处理类

import com.example.demo.two.controller.vo.R;
import com.example.demo.two.util.SimpleResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Resource
    private SimpleResponseUtil simpleResponseUtil;
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        // 未认证,一般返回401
        log.info("未认证: {}", authException.getMessage());
        simpleResponseUtil.write(response, R.fail("未认证,请先认证"));
    }
}

自定义过滤器,以便识别用户身份,把用户身份设置到线程上下文中

import com.example.demo.two.controller.LoginController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

@Slf4j
@Component
public class AuthFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 比如请求头中有个header叫token,放置了认证后的请求头
        String token = request.getHeader("token");
        log.info("用户token:{}", token);
        if (StringUtils.hasText(token)) {
            // 验证token是否已经登录了的用户的token,用户的token临时放在了LoginController
            UserDetails userDetails = LoginController.TOKEN_USERNAME.get(token);
            if (Objects.nonNull(userDetails)) {
                // 有,表示token是对的,设置线程上下文认证信息,然后访问其他资源时,security就会放行
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        filterChain.doFilter(request, response);
    }
}

验证效果

未认证前访问首页:
在这里插入图片描述
在这里插入图片描述

登录认证:

在这里插入图片描述
认证成功后,把得到的token放入请求头中,再请求
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

mysql报错:Column Count Doesn‘t Match Value Count at Row 1

mysql中执行insert、update、delete报错&#xff1a;Column Count Doesnt Match Value Count at Row 1 的解决方案 通常情况&#xff1a;字段不匹配 如&#xff1a;student有id, name, age字段 -- 错误写法 INSERT INTO student VALUES(5,horse)-- 正确写法 INSERT INTO stu…

【JavaEE】CAS(Compare And Swap)操作

文章目录 什么是 CASCAS 的应用如何使用 CAS 操作实现自旋锁CAS 的 ABA 问题CAS 相关面试题 什么是 CAS CAS&#xff08;Compare and Swap&#xff09;是一种原子操作&#xff0c;用于在无锁情况下保证数据一致性的问题。它包含三个操作数——内存位置、预期原值及更新值。在执…

ElasticSearch 10000条查询数量限制

一、前言 我们将库存快照数据导入ES后发现要分页查询10000条以后的记录会报错&#xff0c;这是因为ES通过index.max_result_window这个参数控制能够获取数据总数fromsize最大值&#xff0c;默认限制是10000条&#xff0c;因为ES考虑到数据要从其它节点上报到协调节点如果搜索请…

【Java 进阶篇】MySQL启动与关闭、目录结构以及 SQL 相关概念

MySQL 服务启动与关闭 MySQL是一个常用的关系型数据库管理系统&#xff0c;通过启动和关闭MySQL服务&#xff0c;可以控制数据库的运行状态。本节将介绍如何在Windows和Linux系统上启动和关闭MySQL服务。 在Windows上启动和关闭MySQL服务 启动MySQL服务 在Windows上&#x…

基于Java的毕业设计管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

Vue iconfont-阿里巴巴矢量图标库用法

一、vue使用 选择心仪的图标 加入购物车 点击右上角购物车&#xff0c;点击添加至项目 在资源管理 可以看到我的项目 进入项目设置勾选彩色 点击下载到本地 解压压缩包 在main.js文件内导入css文件 import "/assets/font_icon/iconfont.css"; 使用&#xff1a; 复…

第80步 时间序列建模实战:GRNN回归建模

基于WIN10的64位系统演示 一、写在前面 这一期&#xff0c;我们使用Matlab进行GRNN模型的构建。 使用的数据如下&#xff1a; 采用《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal Syndrom…

嵌入式学习笔记(36)什么是定时器

7.1.1定时器是SoC中常见外设 (1)定时器与计数器。计数器是用来计数的&#xff08;每隔一个固定时间会计一个数&#xff09;&#xff1b;因为计数器的计数时间周期是固定的&#xff0c;因此到了一定时间只要用计数值*技术实践周期&#xff0c;就能得到一个时间段&#xff0c;这…

计算机网络笔记 第一章 概述

课程链接 https://www.bilibili.com/video/BV1c4411d7jb/?spm_id_from333.337.search-card.all.click 1.2 因特网概述 网络、互联网与因特网的区别与关系 若干节点和链路互相形成网络若干网络通过路由器互联形成互联网因特网是当今世界上最大的互联网 我们有时并没有严格区…

ARM底层汇编基础指令

汇编语言的组成 伪操作 不参与程序执行&#xff0c;但是用于告诉编译器程序怎么编译.text .global .end .if .else .endif .data 汇编指令 编译器将一条汇编指令编译成一条机器码&#xff0c;在内存里一条指令占4字节内存&#xff0c;一条指令可以实现一个特定的功能 伪指令 不…

三翼鸟三周年:三次升级,全面引领

被誉为“竞争战略之父”的迈克尔波特&#xff0c;曾提出过“差异化竞争”的理念。 简单说&#xff0c;企业在“差异化竞争”中要做到三大法则&#xff1a; 人无我有、人有我优、人有我新。 在许多优秀企业的身上&#xff0c;都能看到差异化的影子&#xff0c;比如华为、海尔…

基于Java的校园书法绘画比赛评分系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言管理员功能评委功能参赛者功能具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新…

EasyExcel 优雅实现 Excel 导入导出

一、简介 EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、内存的等因素的情况下&#xff0c;快速完成Excel的读、写等功能。 二、特点 快速 快速的读取excel中的数据。 简洁 映射excel和实体类&#xff0c;让代码变…

26967-2011 一般用喷油单螺杆空气压缩机

声明 本文是学习GB-T 26967-2011 一般用喷油单螺杆空气压缩机. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了一般用喷油单螺杆空气压缩机(以下简称"单螺杆空压机")的术语和定义、型号、基本 参数、要求、试验方法、…

Matlab坐标轴标签中文设置宋体

对y坐标输出中文宋体 新罗马字符 x[1,2,3,4,5,6,7]; plot(x) ylabel(\fontname{宋体}\fontsize{20}长度\fontname{Times New Roman}\fontsize{10} (μm))可以灵活设置字体和大小,其图片如下图所示 也可以对全图的文字设置同一个字体 set(gca,FontSize,9,Fontname, Times New…

[vue-admin-template实战笔记]

1.克隆项目 git clone gitgitee.com:panjiachen/vue-admin-template.git 2.安装依赖 npm install 3.运行项目就会自动打开网页&#xff0c;并且热部署插件 npm run dev 4.查看代码 //将vue-admin-template拖入到idea中即可查看代码 1)并且发现&#xff0c;常用的东西已经集…

c# 委托 事件 lambda表达式

委托 C/C中的函数指针实例&#xff1a; typedef int (*Calc)(int a, int b); //这里必须加括号 int Add(int a, int b) {return a b; } int Sub(int a, int b) {return a - b; } int main() {int x 100;int y 200;int z 0;Calc funcPoint1 &Add;Calc funcPoint2 &am…

【实践成果】Splunk 9.0 Configuration Change Tracking

Splunk 9.0 引入了新的功能&#xff0c;一个很重要的一个&#xff0c;就是跟踪conguration 文件的变化&#xff1a; 这个很重要的特性&#xff0c;在splunk 9.0 以后才引入&#xff0c;就看server.conf 配置中&#xff0c;9.0 以后的版本才有&#xff1a; server.conf - Splu…

【STL巨头】set、map、multiset、multimap的介绍及使用

set、map、multiset、multimap的介绍及使用 一、关联式容器二、键值对键值对概念定义 三、setset的介绍set的使用set的模板参数列表set的构造set的迭代器set的容量emptysize set的修改操作insertfind && erasecountlower_bound 和 upper_bound Multiset的用法 四、mapm…

WebGL笔记:绘制矩形面的几种方式以及封装封装多边形对象来绘制不同图形

绘制矩形面 可以绘制的面只有三角面&#xff0c;要绘制矩形面的话&#xff0c;只能用两个三角形去拼 1 &#xff09; 使用 三角带 TRIANGLE_STRIP 绘制矩形 回顾一下之前的规律&#xff1a; 第一个三角形&#xff1a;v0>v1>v2第偶数个三角形&#xff1a;以上一个三角形…