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
{@Autowiredprivate AccessTokenFilter accessTokenFilter;@Overrideprotected 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);}@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();     // 这个会对用户提交的密码进行编码,然后才跟密码库里面的密码进行比较}@Bean@Overridepublic 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","/"})@ResponseBodypublic 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 
{@Autowiredprivate AuthenticationManager authenticationManager;@GetMapping(value = "/hello")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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));}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return authorities;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUserName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic 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
{@Overrideprotected 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
{@Overridepublic 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.mzph.cn/diannao/32936.shtml

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

相关文章

嘉楠勘智CanMV-K230的大小核如何操作

摘要&#xff1a;嘉楠勘智CanMV-K230的帮助文档、例子模型说明中&#xff0c;一直在提“大核&#xff0c;小核”&#xff0c;还提到将文件复制到小核并解压&#xff0c;然后在大核中操作&#xff0c;本文介绍一下这两个“核”如何操作。 所需的硬件&#xff1a;CanMV-K230-V1.1…

<router-view />标签的理解

< router-view />标签的理解 < router-view />用来承载当前级别下的子集路由的一个视图标签。显示当前路由级别下一级的页面。 App.vue是根组件&#xff0c;在它的标签里使用&#xff0c;而且配置好路由的情况下&#xff0c;就能在浏览器上显示子组件的效果。 如…

Python题目

实例 3.1 兔子繁殖问题&#xff08;斐波那契数列&#xff09; 兔子从出生后的第三个月开始&#xff0c;每月都会生一对兔子&#xff0c;小兔子成长到第三个月后也会生一对独自。初始有一对兔子&#xff0c;假如兔子都不死&#xff0c;那么计算并输出1-n个月兔子的数量 n int…

皇河将相董事长程灯虎出席第二十四届世纪大采风并获奖

仲夏时节,西子湖畔。第二十四届世纪大采风品牌人物年度盛典于6月16日至17日在杭州东方文化园隆重举行。本届盛典由亿央网、《华夏英才》电视栏目联合多家媒体共同主办,中世采文化发展集团承办,意尔康股份有限公司、宸咏集团协办,汇聚了来自全国政、商、产、学、研、媒等各界代表…

Eureka 服务注册与发现

目录 前言 注册中心 CAP 理论 常⻅的注册中心 CAP理论对比 Eureka 搭建 Eureka Server 引⼊ eureka-server 依赖 完善启动类 编写配置⽂件 启动服务 服务注册 引⼊ eureka-client 依赖 完善配置⽂件 启动服务 服务发现 引⼊依赖 完善配置⽂件 远程调⽤ 启动…

昇思25天学习打卡营第2天|张量Tensor

一、张量的定义&#xff1a; 张量是一种特殊的数据结构&#xff0c;与数组和矩阵非常相似。张量&#xff08;Tensor&#xff09;是MindSpore网络运算中的基本数据结构&#xff08;也是所有深度学习模型的基础数据结构&#xff09;&#xff0c;下面将主要介绍张量和稀疏张量的属…

企业中订单超时关闭是怎么做的?我说用延迟消息,面试官让我回去等消息?

文章目录 背景时序图方案对比方案一 被动关闭方案二 定时关闭方案三 Rocket MQ延迟消息 总结 背景 订单超时未支付是电商中的一个核心场景&#xff0c;当用户创建订单后&#xff0c;超过一定时间没有支付&#xff0c;平台需要及时将该订单关闭。需要关闭的主要原因有以下几个&…

【database1】mysql:DDL/DML/DQL,外键约束/多表/子查询,事务/连接池

文章目录 1.mysql安装&#xff1a;存储&#xff1a;集合&#xff08;内存&#xff1a;临时&#xff09;&#xff0c;IO流&#xff08;硬盘&#xff1a;持久化&#xff09;1.1 服务端&#xff1a;双击mysql-installer-community-5.6.22.0.msi1.2 客户端&#xff1a;命令行输入my…

RTSP协议分析与安全实践

RTSP协议&#xff0c;全称实时流协议(Real Time Streaming Protocol)&#xff0c;前文已经简单介绍了RTSP相关协议&#xff1b; RTSP和RTP(RTCP) 这里再提一下RTSP和RTP/RTCP、RSVP的关系&#xff1b;如图&#xff1a; RTSP和HTTP 相似性&#xff1a;RTSP和HTTP协议都使用纯…

Android,RPC原理,C语言实现Binder跨进程通信Demo

RPC原理图 Binder C语言层的Demo演示 新建目录 把两个文件拷贝到我们的Demo下面 1.binder_server.c #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <linux/types.h> #include <stdbool.h> #include <string.h> #…

多路h265监控录放开发-(12)完成全部开始录制和全部停止录制代码

xviewer.h 新增 public: void StartRecord();//126 开始全部摄像头录制 void StopRecord();//126 停止全部摄像头录制 xviewer.cpp 新增 //视频录制 static vector<XCameraRecord*> records;//126void XViewer::StartRecord() //开始全部摄像头录制 126 {StopRecord…

基于JSP的“塞纳河畔左岸”的咖啡馆管理系统

开头语&#xff1a; 塞纳河畔左岸的咖啡&#xff0c;我手一杯品尝的你美~ 哎哟&#xff0c;不错哦&#xff01;我们今天来介绍一下咖啡馆管理系统&#xff01; 你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果你对咖啡馆管理系统感兴趣或有相关需求&#xff0c;欢迎联…

AGV机器人的调度开发分析(1)- 内核中的路线规划

准备开始写一个系列&#xff0c;介绍下AGV机器人的调度的开发和应用。 按照openTCS的核心内容&#xff0c;国内多家广泛应用于AGV的调度。那么架构图如下&#xff1a; Kernel中有一个是Routing&#xff0c;这是路由规划模块&#xff0c;需要实现的细节功能包括如下&#xff1a…

Django 模版转义

1&#xff0c;模版转义的作用 Django模版系统默认会自动转义所有变量。这意味着&#xff0c;如果你在模版中输出一个变量&#xff0c;它的内容会被转义&#xff0c;以防止跨站脚本攻击&#xff08;XSS&#xff09;。例如&#xff0c;如果你的变量包含HTML标签&#xff0c;这些…

长亭谛听教程部署和详细教程

PPT 图片先挂着 挺概念的 谛听的能力 hw的时候可能会问你用过的安全产品能力能加分挺重要 溯源反制 反制很重要感觉很厉害 取证分析 诱捕牵制 其实就是蜜罐 有模板直接爬取某些网页模板进行伪装 部署要求 挺低的 对linux内核版本有要求 需要root 还有系统配置也要修改 …

网络编程篇:HTTP协议

一.预备知识 在客户端访问服务端时&#xff0c;要用ipport&#xff0c;但是在日常用户访问服务端的时候&#xff0c;并不会直接使用ip&#xff0c;而是使用域名&#xff0c;比如&#xff1a;百度(www.baidu,com)。 …

智能优化算法改进策略之局部搜索算子(八)--Powell方法

1、原理介绍 Powell方法[1]是一种无约束优化算法&#xff0c;又称为方向加速法&#xff0c;用于寻找多变量函数的极小值。其基本思想是在迭代中逐次产生Q共轭方向组&#xff0c;本质上它属于不需计算导数的共轭方向法。每次迭代后&#xff0c;算法会更新搜索方向&#xff0c;并…

Java内存泄漏检测和分析介绍

在Java中&#xff0c;内存泄漏检测和分析是一个重要的任务&#xff0c;可以通过以下几种方式进行&#xff1a; 1. 使用VisualVM VisualVM是一个可视化工具&#xff0c;可以监控、分析Java应用程序的内存消耗。它可以显示堆内存、垃圾收集、线程等信息&#xff0c;并且可以对内…

英伟达下一代DLSS或利用人工智能

英伟达的黄仁勋在2024年Computex展会上的问答环节中&#xff0c;提前透露了公司未来几代深度学习超采样&#xff08;DLSS&#xff09;技术的发展方向。在回答有关DLSS的问题时&#xff0c;黄仁勋表示&#xff0c;未来我们将看到通过纯粹的人工智能生成的纹理和对象。他还提到&a…

构建下一代数据解决方案:SingleStore、MinIO 和现代 Datalake 堆栈

SingleStore 是专为数据密集型工作负载而设计的云原生数据库。它是一个分布式关系 SQL 数据库管理系统&#xff0c;支持 ANSI SQL&#xff0c;并因其在数据引入、事务处理和查询处理方面的速度而受到认可。SingleStore 可以存储关系、JSON、图形和时间序列数据&#xff0c;以满…