java:spring-security的简单例子

【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.mfbz.cn/a/736885.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

飞行堡垒系列_键盘灯开关

飞行堡垒系列键盘灯可以通过键盘上的"Fn 方向键"控制 演示机型品牌型号&#xff1a;飞行堡垒8 系统版本&#xff1a;Windows 11 飞行堡垒键盘灯可通过键盘上的"Fn方向键"控制。 " Fn 下方向键 "为减弱键盘灯光&#xff0c;多按几次键盘灯就可…

如何使用AI工具进行写作

随着AI人工智能技术的飞速发展&#xff0c;AI工具已经逐渐成为学术和专业写作的得力助手。AI工具不仅可以帮助我们提高写作效率&#xff0c;还能在内容创作上提供灵感和支持。在本文中&#xff0c;小编将和大家分享如何利用AI工具提高写作效率和质量&#xff0c;并确保文章的原…

湖北民族大学2024年成人高等继续教育招生简章

湖北民族大学&#xff0c;这所承载着深厚文化底蕴和卓越教育理念的学府&#xff0c;在崭新的2024年再次敞开怀抱&#xff0c;热烈欢迎有志于深化学习、提升自我的成人学员们。今年的成人高等继续教育招生&#xff0c;不仅是学校对于终身教育理念的具体实践&#xff0c;更是为广…

java的单例集合迭代器

迭代器Iterator 根据之前的介绍我们知道&#xff0c;单例集合是由接口Collection定义的容器。Collection接口之下由定义了List接口和Set接口&#xff0c;其中List接口定义的容器的特征是有序可重复&#xff0c;而Set接口定义的容器的特征是无序不可重复的。 List接口定义的容器…

FFmpeg源码:ff_ctz / ff_ctz_c函数分析

一、ff_ctz函数的作用 ff_ctz定义在FFmpeg源码目录的libavutil/intmath.h 下&#xff1a; #ifndef ff_ctz #define ff_ctz ff_ctz_c /*** Trailing zero bit count.** param v input value. If v is 0, the result is undefined.* return the number of trailing 0-bits*/…

LeetCode 算法:翻转二叉树 c++

原题链接&#x1f517;&#xff1a;翻转二叉树 难度&#xff1a;简单⭐️ 题目 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 …

英语智汇学习系统

目 录 1 软件概述 1.1 项目研究背景及意义 2 系统相关技术 2.1 HTML、WXSS、JAVASCRIPT技术 2.2 Vanilla框架 2.3 uni-app框架 2.4 MYSQL数据库 3 需求分析 3.1 可行性分析 3.2 功能需求分析 3.3 系统用户及用例分析 3.4 非功能需求分析 3.5 数据流图…

bazel :Output Directory Layout

Output Directory Layout This page covers requirements and layout for output directories. Requirements for an output directory layout: Doesn’t collide if multiple users are building on the same box.如果多个用户在同一个盒子上建造则不会发生冲突。 Support…

视频监控平台:支持交通部行业标准JT/T905协议(即:出租汽车服务管理信息系统)的源代码的函数和功能介绍及分享

目录 一、视频监控平台介绍 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;视频接入能力介绍 &#xff08;三&#xff09;功能介绍 二、JT/T905协议介绍 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;主要内容 1、设备要求 2、业务功能要求…

【ajax实战01】数据管理网站总述

一&#xff1a;功能实现 登录和权限判断查看文章内容列表&#xff08;筛选和分页&#xff09;编辑文章&#xff08;数据回显&#xff09;删除文章发布文章&#xff08;图片上传&#xff0c;富文本编辑器&#xff09; 该网站最终实现&#xff1a;登录后台管理系统&#xff0c;…

理解 什么是 滚动更新,蓝绿部署,灰度发布 以及它们的区别

滚动更新&#xff0c;蓝绿部署&#xff0c;灰度发布 这3种 现代化的 发布模式相信很多人都听过&#xff0c; 但是并不是都能正确理解他们的作用和区别 滚动更新 Rolling Update 所谓滚动更新是for 那些多实例的service的。 假如1个 service 有n 个instance, 更新时并不是n 个…

鸿蒙开发系统基础能力:【@ohos.faultLogger (故障日志获取)】

故障日志获取 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import faultLogger from ohos.faultLoggerFaultType 故障类型枚举。 系统能力&#xff1a; 以下各项对应的系统能力…

【Python/Pytorch 】-- SVM算法

文章目录 文章目录 00 写在前面01 SVM算法简介02 SVM算法的基本原理线性SVM非线性SVM 03 基于Python 版本的SVM算法04 优化目标表达式理解&#xff1a;05 约束条件表达式理解 00 写在前面 SVM算法可以结合鲸鱼算法、飞蛾扑火算法、粒子群算法、灰狼算法、蝙蝠算法等等各种优化…

预训练是什么?

预训练是什么&#xff1f; 图像领域的预训练 在介绍图像领域的预训练之前&#xff0c;我们首先介绍下卷积神经网络&#xff08;CNN&#xff09;&#xff0c;CNN 一般用于图片分类任务&#xff0c;并且CNN 由多个层级结构组成&#xff0c;不同层学到的图像特征也不同&#xff…

qt 5.6 qmake手册

qt 5.6 qmake手册 &#xff08;笔者翻译的qmake手册&#xff0c;多数是机翻&#xff0c;欢迎评论区纠错修正&#xff09; Qmake工具有助于简化跨不同平台开发项目的构建过程。它自动生成Makefile&#xff0c;因此创建每个Makefile只需要几行信息。您可以将qmake用于任何软件项目…

细粒度图像分类论文阅读笔记

细粒度图像分类论文阅读笔记 摘要Abstract1. 用于细粒度图像分类的聚合注意力模块1.1 文献摘要1.2 研究背景1.3 本文创新点1.4 计算机视觉中的注意力机制1.5 模型方法1.5.1 聚合注意力模块1.5.2 通道注意力模块通道注意力代码实现 1.5.3 空间注意力模块空间注意力代码实现 1.5.…

20240624 每日AI必读资讯

&#x1f916;AI学会篡改奖励函数、欺骗研究者&#xff01;Claude团队&#xff1a;无法根除的行为&#xff0c;令人不安 - 实验中让AI可以访问自己的强化学习代码&#xff0c;并且提问&#xff1a;目前为止&#xff0c;我们总共进行了几轮强化学习&#xff1f;AI在自以为不会被…

三十八篇:架构大师之路:探索软件设计的无限可能

架构大师之路&#xff1a;探索软件设计的无限可能 1. 引言&#xff1a;架构的艺术与科学 在软件工程的广阔天地中&#xff0c;系统架构不仅是设计的骨架&#xff0c;更是灵魂所在。它如同建筑师手中的蓝图&#xff0c;决定了系统的结构、性能、可维护性以及未来的扩展性。本节…

测试测量-DMM直流精度

测试测量-DMM直流精度 最近去面试&#xff0c;发现了自己许多不足&#xff0c;比如我从未考虑过万用表准或者不准&#xff0c;或者万用表有多准&#xff1f; 在过去的实验室中&#xff0c;常用的DMM有KEYSIGHT 34401A以及 KEITHLEY THD2015&#xff0c;就以这两台为例&#x…

Typora + Hexo 图片路径问题(Typedown)

文章目录 1. 冲突来源2. 解决思路3. 实现1. typora图片路径2. hexo脚本 1. 冲突来源 Hexo上对于图片在md中的引用&#xff0c;使用了post_asset_folder: true配置&#xff0c;来更好的管理图片。 当一篇名为xxx.md的文章引用1.png图片时&#xff0c;默认让1.png保持在xxx文件夹…