Spring Security进行登录认证和授权
一、Spring Security内部认证流程
用户首次登录提交用户名和密码后spring security 的UsernamePasswordAuthenticationFilter把用户名密码封装Authentication对象
然后内部调用ProvideManager的authenticate方法进行认证,然后ProvideManager进一步通过内部调用DaoAuthencationPriovider的authenticate方法进行认证
DaoAuthencationPriovider通过调用UserDetailsService的实现类InMemoryDetialManager的loadUserByUsername方法查询用户,并把查询到的用户信息封装成一个UserDetails对象。注意InMemoryDetialManage是从内存中查询用户信息的,所以我们要定制化实现UserDetailsService接口来达到从数据库查询用户信息。
通过PasswordEncode对比返回的UserDetails对象的密码和Authentication对象的密码是否一致,如果密码正确就把UserDetails中的权限信息设置到Authentication对象中
如果成功返回Authentication对象就使用SecurityContextHolder.getContext().setAuthentication方法存储改对象,然后其他过滤器就会通过SecurityContextHolder来获取当前用户信息。
二、自定义认证流程
我们要定制化实现UserDetailsService接口来达到从数据库查询用户信息,所以重新写一个UserDetailsServiceImpl实现类。然后认证通过使用用户id生成一个jwt(也就是token),然后用userId 作为key,用户信息作为value存入redis中。用户第一次登陆认证流程如下图。
导入依赖
<!--SpringSecurity启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!--redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--fastjson依赖--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><!--jwt依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId></dependency>
UserDetailsServiceImpl代码
@Service
public class UserDetailServiceImpl implements UserDetailsService {@Resourceprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名查询用户信息LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUserName,username);User user = userMapper.selectOne(queryWrapper);//判断是否查到用户 如果没查到抛出异常if(Objects.isNull(user)){throw new RuntimeException("用户名不存在");}//返回用户信息// TODO 查询权限信息封装return new LoginUser(user);}
}
BlogLoginServiceImpl 登陆认证代码
@Service
public class BlogLoginServiceImpl implements BlogLoginService {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate RedisCache redisCache;@Overridepublic ResponseResult login(User user) {UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());Authentication authenticate = authenticationManager.authenticate(authenticationToken);//判断是否认证通过if(Objects.isNull(authenticate)){throw new RuntimeException("用户名或密码错误");}//获取userid 生成tokenLoginUser loginUser = (LoginUser) authenticate.getPrincipal();String userId = loginUser.getUser().getId().toString();String jwt = JwtUtil.createJWT(userId);//把用户信息存入redisredisCache.setCacheObject("bloglogin:"+userId,loginUser);//把token和userinfo封装 返回UserInfoVo userInfoVo = BeanCopyUtil.copyBean(loginUser.getUser(), UserInfoVo.class);BlogUserLoginVo blogUserLoginVo = new BlogUserLoginVo(jwt, userInfoVo);//把User转换成UserInfoVoreturn ResponseResult.okResult(blogUserLoginVo);}
}
SecurityConfig配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers("/login").anonymous()// 除上面外的所有请求全部不需要认证即可访问.anyRequest().permitAll();http.logout().disable();//允许跨域http.cors();}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}
JwtUtil工具类
public class JwtUtil {//有效期为public static final Long JWT_TTL = 24*60 * 60 *1000L;// 60 * 60 *1000 一个小时//设置秘钥明文public static final String JWT_KEY = "hhh";public static String getUUID(){String token = UUID.randomUUID().toString().replaceAll("-", "");return token;}/*** 生成jtw* @param subject token中要存放的数据(json格式)* @return*/public static String createJWT(String subject) {JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间return builder.compact();}/*** 生成jtw* @param subject token中要存放的数据(json格式)* @param ttlMillis token超时时间* @return*/public static String createJWT(String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间return builder.compact();}private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;SecretKey secretKey = generalKey();long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if(ttlMillis==null){ttlMillis=JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);return Jwts.builder().setId(uuid) //唯一的ID.setSubject(subject) // 主题 可以是JSON数据.setIssuer("sg") // 签发者.setIssuedAt(now) // 签发时间.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥.setExpiration(expDate);}/*** 创建token* @param id* @param subject* @param ttlMillis* @return*/public static String createJWT(String id, String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间return builder.compact();}public static void main(String[] args) throws Exception {String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";Claims claims = parseJWT(token);System.out.println(claims);}/*** 生成加密后的秘钥 secretKey* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 解析** @param jwt* @return* @throws Exception*/public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}}
redis缓存配置类
@Configuration
public class RedisConfig {@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}
}
下次用户再次发请求时带token(相对于没有前后端分离时的session)来进行访问,如果没有携带则不让访问,如果有就经过一个jwt认证过滤器来获取token,解析token,最后获取userId,封装成Authentication对象存入SecurityContextHolder和redis中以便其他过滤器或者资源来获取当前用户请求信息
最后感悟:springboot约定大于配置,许多接口都可以自定义,达到定制化功能,这也体现了springboot的强大,还有许多功能还没完成,授权功能和流程将在后续完成,敬请期待。
Spring Security进行登录认证和授权相关推荐
- spring security+jwt 登录认证
spring security+jwt 登录认证 1.综述 2.版本与环境 3.架构 4.数据库认证逻辑图 5.案例 security+jwt 5.1引入依赖 5.2新建工具类 5.2新建组件类 5. ...
- Spring Security + JWT 实现认证和授权
整体流程 用户登录成功后,服务端返回 jwt token 给客户端.token 中包含用户名,失效时间等信息.客户端保存 token,服务端不进行存储.客户端每次发送请求的时候,带上 token,服务 ...
- 清晰搞懂Spring Security的登录认证
文章目录 前言 1.简介 2.登录认证 2.1.过滤器链 2.2.认证流程 2.3.大思路分析 3.撸袖开干 3.1.环境搭建 3.1.1.数据库 3.1.2.项目搭建 3.1.3.工具类.部分配置类 ...
- Spring Security OAuth2.0认证授权
文章目录 1.基本概念 1.1.什么是认证 1.2 什么是会话 1.3什么是授权 1.4授权的数据模型 1.4 RBAC 1.4.1 基于角色的访问控制 2.基于Session的认证方式 3.整合案例 ...
- 自定义request_Spring Security 自定义登录认证(二)
一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Se ...
- Spring Security 实战干货:OAuth2授权回调的核心认证流程
1. 前言 我们在上一篇 Spring Security 实战干货:OAuth2 授权回调的处理机制 对 OAuth2 服务端调用客户端回调的流程进行了图解, 今天我们来深入了解 OAuth2 在回调 ...
- Spring Security OAuth2.0认证授权知识概括
Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...
- 从零开始超详细的Spring Security OAuth2.0实现分布式系统授权(注册中心+网关+认证授权服务(JWT令牌验证)+资源调用服务)
文章目录 一.OAuth2.0 1.介绍 2.例子 3.执行流程 二.Spring Cloud Security OAuth2 1.环境介绍 2.认证流程 三.整合分布式项目 1.技术方案 2.项目结 ...
- Spring Security OAuth2.0认证授权五:用户信息扩展到jwt
历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] [Spring Securi ...
最新文章
- 搜索引擎索引之如何更新索引
- 漫反射 高光反射_如何有效地使用反射
- linux ftp mysql_linux下ftp和ftps以及ftp基于mysql虚拟用户认证服务器的搭建
- Socket简单介绍(通过NSStream)
- 担心再次被起诉?马斯克已删除特斯拉可能几个月内成为最大公司推文
- css随机数,在LESS CSS中生成随机数?
- zookeeper和eureka的对比
- Opencv实现多幅图片拼接在一起
- 2010——满地遍是网页防篡改和WAF
- PDF如何复制页面,PDF复制页面这种方法就够了
- 最新Everest 企业版(AIDA64)使用SQL保存数据的方法介绍
- HTML+CSS实战:做一个京东登录页面
- 认识VBA------------------VBA基础
- 单点登录原理与代码实现
- TKMybatis的使用大全和例子(example、Criteria、and、or)
- Win7下给电脑加域,及遇到的问题。欢迎留言
- FC游戏 《三国志2-霸王的大陆》攻略
- 小学4年级计算机课作业题目,部编版小学语文四年级下册课堂同步作业试题及答案(全册).doc...
- HTML+CSS静态页面网页设计作业——甜品奶茶店(19页) HTML5网页设计成品_学生DW静态网页设计_web课程设计网页制作
- wps文字 用WPS如何演示制作英语有声读物
热门文章
- 青云科技以开放姿态打造低代码平台
- 百病之源五脏为根(国粹---gt;中医;在此提倡自然疗法!)
- android 组件导出,阳光沙滩-android组件化项目,build生成的文件重复的情况
- java.lang.NoSuchMethodError的解决办法
- 【FI】SAP标准成本计算和发布
- Maven3.5.3下载安装与环境配置
- c语言编程转化为vba编程,PPT VBA编程.doc
- Qt: 表格(QTableWidget)隐藏自动生成的序号列(行头)
- java中获取输入的几种方式
- 每日一道leetcode(python)695. 岛屿的最大面积