java:spring-security的简单例子

news2024/11/18 1:43:25

【pom.xml】

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.3.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>2.3.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.3.12.RELEASE</version>
</dependency>

【/resources/public/login.html】

<!DOCTYPE html>
<html>
    <head>
        <title>Spring Security Example </title>
    </head>
    <body>
        <form action="/security/login2" method="get">
            <div><label> User Name : <input type="text" name="username" value="user"/> </label></div>
            <div><label> Password: <input type="password" name="password" value="123"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>

【SecurityConfig.java】

package com.chz.mySpringSecurity.config;

import com.chz.mySpringSecurity.filter.AccessTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    @Autowired
    private AccessTokenFilter accessTokenFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http.authorizeRequests()
                //访问"/"和"/home"路径的请求都允许
                .antMatchers(
                        "/",            // 这个会跳转到主页,要放过
                        "/home",           // 这是主页的地址,要放过
                        "/security/login2"          // 这个是提交登录的地址,要放过
                )
                .permitAll()
                //而其他的请求都需要认证
                .anyRequest()
                .authenticated()
                .and()
                //修改Spring Security默认的登陆界面
                .formLogin()
                .loginPage("/security/login")       // 登录地址是【/security/login】
                .permitAll()
                .and()
                .logout()
                .permitAll();

        http.addFilterBefore(accessTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();     // 这个会对用户提交的密码进行编码,然后才跟密码库里面的密码进行比较
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

【PublicController.java】

package com.chz.mySpringSecurity.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Slf4j
@Controller
@RequestMapping("/")
public class PublicController
{
    @GetMapping(value = {"/home","/"})
    @ResponseBody
    public String home(){
        log.info("chz >>> SecurityController.home(): ");
        return "this is home page!";
    }
 
}

【SecurityController.java】

package com.chz.mySpringSecurity.controller;

import com.chz.mySpringSecurity.entity.LoginUser;
import com.chz.mySpringSecurity.entity.LoginUsers;
import com.chz.mySpringSecurity.entity.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;

@Slf4j
@Controller
@RequestMapping("/security")
public class SecurityController 
{
    @Autowired
    private AuthenticationManager authenticationManager;

    @GetMapping(value = "/hello")
    @ResponseBody
    public String hello(){
        log.info("chz >>> SecurityController.hello(): ");
        return "this is hello page!";
    }

 
    @GetMapping(value = "/login")
    public String login(HttpServletRequest request, HttpServletResponse response){
        log.info("chz >>> SecurityController.login(): ");
        return "/login.html";
    }

    @GetMapping(value = "/login2")
    @ResponseBody
    public ResponseResult login2(@RequestParam String username, @RequestParam String password)
    {
        log.info("chz >>> SecurityController.login2(): username={}, password={}", username, password);

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);      // 这个会触发【UserDetailsService.loadUserByUsername(String username)】方法被调用
        if(Objects.isNull(authenticate)){
            log.info("chz >>> SecurityController.login2(): 用户名或密码错误");
            throw new RuntimeException("用户名或密码错误");
        }

        LoginUser loginUser = (LoginUser)authenticate.getPrincipal();
        loginUser.setRefreshToken(Math.abs(ThreadLocalRandom.current().nextLong())+"");
        loginUser.setAccessToken(Math.abs(ThreadLocalRandom.current().nextLong())+"");
        LoginUsers.users.put(loginUser.getAccessToken(), loginUser);
        log.info("chz >>> accessToken: " + loginUser.getAccessToken());

        // context里面设置了【authenticationToken】就表示用户已经登录了,但是这个是根据cookie里面有sessionId判断的,跟accessToken无关
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        return new ResponseResult(200,"登陆成功", loginUser.getAccessToken());
    }

    @GetMapping(value = "/logout2")
    @ResponseBody
    public ResponseResult logout2(@RequestParam String accessToken)
    {
        log.info("chz >>> SecurityController.login2(): accessToken={}", accessToken);
        LoginUsers.users.remove(accessToken);

        // context里面清除了【authenticationToken】就表示用户已经退出登录了,但是这个是根据cookie里面有sessionId判断的,跟accessToken无关
        SecurityContextHolder.getContext().setAuthentication(null);

        return new ResponseResult(200,"退出成功");
    }
}

【LoginUser.java】

package com.chz.mySpringSecurity.entity;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.HashSet;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails
{
    private String accessToken;
    private String refreshToken;
    private HashSet<GrantedAuthority> authorities = new HashSet<>();

    private User user;

    public LoginUser(User user)
    {
        this.user = user;
    }

    public void addAuthority(String authority)
    {
        authorities.add(new SimpleGrantedAuthority(authority));
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

 
    @Override
    public String getPassword() {
        return user.getPassword();
    }

 
    @Override
    public String getUsername() {
        return user.getUserName();
    }

 
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

 
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

 
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

 
    @Override
    public boolean isEnabled() {
        return true;
    }
}

【LoginUsers.java】

package com.chz.mySpringSecurity.entity;

import java.util.concurrent.ConcurrentHashMap;

public class LoginUsers
{
    // 这个map模拟的是分布式缓存redis的数据,代表已登录的用户列表
    public static ConcurrentHashMap<String, LoginUser> users = new ConcurrentHashMap<>();
}

【ResponseResult.java】

package com.chz.mySpringSecurity.entity;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ResponseResult<T> {

    private Integer code;
    private String msg;
    private T data;

 
    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

 
    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

 
    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

【User.java】

package com.chz.mySpringSecurity.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable 
{
    private String userName;
    private String password;
}

【MyExceptionHandler.java】

package com.chz.mySpringSecurity.exceptions;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class MyExceptionHandler 
{
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e){
        log.error("chz >>> err", e);
        return "发生异常了";
    }
}

【AccessTokenFilter.java】

package com.chz.mySpringSecurity.filter;

import com.chz.mySpringSecurity.entity.LoginUser;
import com.chz.mySpringSecurity.entity.LoginUsers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
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;

@Slf4j
@Component
public class AccessTokenFilter extends OncePerRequestFilter
{
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
    {
        log.info("chz >>> ChzAuthenticationTokenFilter.doFilterInternal(): uri:{}, queryParam={}", request.getRequestURI(), request.getQueryString());
        LoginUser loginUser = null;
        String accessToken = request.getParameter("accessToken");
        if( !StringUtils.isEmpty(accessToken) ) {
            loginUser = StringUtils.isEmpty(accessToken) ? null : LoginUsers.users.get(accessToken);
        }

        if( loginUser!=null ){
            // 这是登录过的,可以访问受限资源
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, loginUser.getPassword(), loginUser.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }else {
            // 没有accessToken清空掉authentication,不让访问受限资源
            SecurityContextHolder.getContext().setAuthentication(null);
        }
        // 不管有没有登录,后面的【UsernamePasswordAuthenticationFilter】会进行权限判断,也直接放过
        filterChain.doFilter(request, response);
    }
}

【UserRepository.java】

package com.chz.mySpringSecurity.repository;

import com.chz.mySpringSecurity.entity.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.concurrent.ConcurrentHashMap;

public class UserRepository 
{
    // 这个用于模拟数据库里面的可登录用户的信息
    public static ConcurrentHashMap<String, User> users = new ConcurrentHashMap<>();

    static {
        //
        User user = new User();
        user.setUserName("user");
        user.setPassword(new BCryptPasswordEncoder().encode("123"));
        users.put(user.getUserName(), user);
        //
        User admin = new User();
        admin.setUserName("admin");
        admin.setPassword(new BCryptPasswordEncoder().encode("456"));
        users.put(admin.getUserName(), admin);
    }
}

【UserDetailsServiceImpl.java】

package com.chz.mySpringSecurity.service;

import com.chz.mySpringSecurity.entity.LoginUser;
import com.chz.mySpringSecurity.entity.User;
import com.chz.mySpringSecurity.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class UserDetailsServiceImpl implements UserDetailsService
{
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        log.info("chz >>> UserDetailsServiceImpl.loadUserByUsername(): username={}", username);
        User user = UserRepository.users.get(username);
        if( user==null ){
            throw new UsernameNotFoundException("用户名不存在");
        }

        LoginUser loginUser = new LoginUser(user);
        loginUser.addAuthority("chz_role1");
        return loginUser;
    }
}

【MySpringSecurityMain.java】

package com.chz.mySpringSecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

@EnableGlobalMethodSecurity(prePostEnabled = true)
@SpringBootApplication
public class MySpringSecurityMain
{
    public static void main(String[] args)
    {
        SpringApplication.run(MySpringSecurityMain.class, args);
    }
}

运行【MySpringSecurityMain】。

访问【http://localhost:8080/security/hello】 ,可以看到被重定向到登录页面【http://localhost:8080/security/login】
在这里插入图片描述

用户名输入【user】,密码输入【123】,点击【Sign In】登录。
在这里插入图片描述
可以得到一个【accessToken】=【8496842402128172477】
再次访问【http://localhost:8080/security/hello】,可以看到虽然已经登录成功了,但还是被重定向到了登录页面【http://localhost:8080/security/login】。
这是因为【url】里面没有带上【accessToken】,【AccessTokenFilter】自动将用户的登录信息清除了。
带上accessToken再试试试访问【http://localhost:8080/security/hello?accessToken=848234722712551727】
在这里插入图片描述
可以看到访问成功了。

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

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

相关文章

java使用Graphics2D生成图片

UI图 实际图片数据库中只存了一个二维码转的base64的数组,直接导出只有一个二维码 这里使用 Graphics2D 画图 public static void main(String[] args) {// 假设你有一个Base64编码的字符串&#xff0c;它表示一张图片String base64ImageString "/9j/4AAQSkZJRgABAgAAA…

考研数学(4/9):微分方程

微分方程 微分方程是高等数学中一个重要的分支&#xff0c;也是考研数学数一中必考的内容。本章主要介绍微分方程的概念、一阶微分方程、高阶线性微分方程以及微分方程的应用。 1. 微分方程的概念 1.1 微分方程的定义 微分方程 是指包含未知函数及其导数的方程。 更准确地说&am…

【数学建模】——【python库】——【Pandas学习】

专栏&#xff1a;数学建模学习笔记 pycharm专业版免费激活教程见资源&#xff0c;私信我给你发 python相关库的安装&#xff1a;pandas,numpy,matplotlib&#xff0c;statsmodels 总篇&#xff1a;【数学建模】—【新手小白到国奖选手】—【学习路线】 第一卷&#xff1a;【数学…

老板电器 45 年的烹饪经验,浓缩在这款烹饪大模型中

在科技不断进步的时代&#xff0c;人工智能&#xff08;AI&#xff09;迅速成为推动各行各业发展的重要力量。家电行业也不例外&#xff0c;根据 Gartner 的报告预测&#xff0c;到 2024 年&#xff0c;AI 家电市场的规模将达到万亿美元级别。这一预估凸显了智能化在家电行业中…

大猫咪守护LoRA:定制你的大猫私人大猫咪宠物写真合影,某音某书流行款

&#x1f339;大家好&#xff01;我是安琪&#xff01;感谢大家的支持与鼓励。 大猫咪LoRA模型简介 今天应群里同学大猫咪宠物合影写真提议&#xff0c;为大家介绍一款来自作者 沐沐人像合成的主题为大猫咪守护的LoRAl模型&#xff1a;沐沐-大猫咪。这是一款当下在某音、某书…

python-docx 使用xml为docx不同的章节段落设置不同字体

本文目录 前言一、完整代码二、代码详细解析1、处理过程解释(1) 引入库并定义路径(2) 创建docx的备份文件(3) 定义命名空间(4) 打开并处理.docx文件(5) 分析和组织文档结构(6) 设置字体(7) 保存结果前言 本文主要解决的内容,就是为一个docx的不同章节段落设置不同的字体,因为…

【Week-G1】调用官方GAN实现MNIST数字识别,Pytorch框架

文章目录 1. 准备数据1.1 配置超参数1.2 下载数据1.3 配置数据 2. 创建模型2.1 定义鉴别器2.2 定义生成器 3. 训练模型3.1 创建实例3.2 开始训练3.3 保存模型 4. 什么是GAN&#xff08;对抗生成网络&#xff09;? &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学…

追求准确,还是追求举一反三,聊天机器人智能程度的困境 | Chatopera

在为企业客户上线聊天机器人客服的过程中&#xff0c;总会遇到一个问题&#xff0c;这让用户和我们都感到纠结。 到底是追求让机器人能准确的回答问题&#xff0c;还是让机器人可以举一反三的回答问题。 准确的回答问题&#xff0c;就是不容许回答错了&#xff0c;但是这样机…

windows中使用anaconda管理python版本

anaconda下载 python的版本问题实在是很大,版本低了高了都会影响脚本的执行,anaconda工具为此而生,不管是在windows下还是linux下,Anaconda的命令跟操作逻辑都是相同的,窥一斑而知全豹,本文在windows下示例如何使用anaconda anaconda的逻辑就是 他是一个全局的管理者,能创建工…

在Ubuntu中使用ROS搭建PX4 Gazebo 模拟飞行 四旋翼 固定翼

综合了网上很多教程以及踩了很多坑总结下来的教程 Ubuntu安装 此处不在详细说明&#xff0c;网上可随处搜到 ROS安装 感谢鱼香ROS大佬提供一键安装脚本 wget http://fishros.com/install -O fishros && sudo bash fishros 接下来按顺序按 1 1 2 3 1 再次运行 w…

红酒哲学:品味流转时光,探寻生活之深邃奥秘

在繁华的都市中&#xff0c;我们时常被各种声音和色彩所包围&#xff0c;追求着速度与激情。然而&#xff0c;在这喧嚣之中&#xff0c;总有那么一刻&#xff0c;我们渴望静下心来&#xff0c;品味一份不同的宁静与深度。这时&#xff0c;一杯雷盛红酒便成了我们与内心对话的桥…

太赞了!SD AI绘画,热门青衫映雪写真制作,一键出片,轻松复刻!【内含相关模型及ComfyUI工作流】

hello&#xff0c;大家好我是安琪&#xff01; 今天安琪给大家带来了一篇关于写真制作&#xff0c;我通过SD WebUI进行本次青衫映雪主题的写真制作。(相关内容文末可自行扫描获取) 准备工作&#xff1a; 1.大模型准备真人写实大模型&#xff0c;我这里使用了TQing v3.4 2.…

Radxa 学习摘录

文章目录 一、参考资料二、硬件知识 一、参考资料 技术论坛&#xff08;推荐&#xff09; 官方资料下载 wiki资料 u-boot 文档 u-boot 源码 内核文档 内核源码 原理图 二、硬件知识 Radxa 3B 主板概览 MIPI接口 MIPI CSI&#xff08;Camera Serial Interface&#xff09;…

【前端】HTML+CSS复习记录【2】

文章目录 前言一、img&#xff08;图片标签&#xff09;二、a&#xff08;链接标签&#xff09;三、ul&#xff08;无序列表&#xff09;四、ol&#xff08;有序列表&#xff09;系列文章目录 前言 长时间未使用HTML编程&#xff0c;前端知识感觉忘得差不多了。通过梳理知识点…

智慧园区大数据云平台建设方案(Word原件)

第一章 项目建设背景及现状 第二章 园区创新发展趋势 第三章 工业园区大数据存在的问题 第四章 智慧工业园区大数据建设目的 第五章 智慧园区总体构架 第六章 系统核心组件 第七章 智慧工业园区大数据平台规划设计 获取方式&#xff1a;本文末个人名片直接获取。 软件资料清单…

文本生成sql模型(PipableAI/pip-sql-1.3b)

安装环境 pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install transformers 代码 question "What are the email address, town and county of the customers who are of the least common gender?"sc…

three.js - MeshStandardMaterial(标准网格材质)- 金属贴图、粗糙贴图

金属贴图、粗糙贴图 金属贴图&#xff1a;metalnessMap 和 粗糙贴图&#xff1a;roughnessMap&#xff0c;是用于模拟物体表面属性的两种重要贴图技术&#xff0c;这两种贴图&#xff0c;通常与基于物理的渲染&#xff08;PBR&#xff09;材质&#xff08;如&#xff1a;MeshSt…

linux进程是什么?

进程概念 进程Process是指计算机中已运行的程序&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;是操作系统结构的基础。 在早期面向进程设计的计算机结构中&#xff0c;进程是程序的基本执行实体。在当代面向线程设计的计算机结构中&#xff0c;进程是线程的容器…

K210视觉识别模块学习笔记6: 识别苹果_图形化操作函数_

今日开始学习K210视觉识别模块: 图形化操作函数 亚博智能 K210视觉识别模块...... 固件库: canmv_yahboom_v2.1.1.bin 训练网站: 嘉楠开发者社区 今日学习如何在识别到目标的时候添加图形化操作:(获取坐标、框出目标等) 在识别苹果的基础上 学习与添加 这些操…

docker配置国内镜像加速器

1、搜索阿里云 2、搜索容器镜像服务 点击管理控制台 配置镜像加速器