企业网站管理系统登陆,长春网站建设 吉网传媒,致设计,如何自己制作公众号SpringSecurityjwtcaptcha登录认证授权总结
版本信息#xff1a;
springboot 3.2.0、springSecurity 6.2.0、mybatis-plus 3.5.5
认证授权思路和流程#xff1a;
未携带token#xff0c;访问登录接口#xff1a;
1、用户登录携带账号密码
2、请求到达自定义Filterjwtcaptcha登录认证授权总结
版本信息
springboot 3.2.0、springSecurity 6.2.0、mybatis-plus 3.5.5
认证授权思路和流程
未携带token访问登录接口
1、用户登录携带账号密码
2、请求到达自定义Filter自定义Filter(如JwtAuthenticationTokenFilter)继承OncePerRequestFilter(此Filter只会进行一次过滤在请求返回时不会再进行调用)在SecurityConfig中配置将自定义Filter添加到过滤器链中并加在UsernamePasswordAuthenticationFilter过滤器之前
3、自定义Filter逻辑
3.1、查询到用户未携带token直接放行进入到后面认证流程
3.2、将用户信息封装成Authentication对象调用authticate()方法进行验证一直到DaoAuthenticationProvider中会调用UserDetailService的loadUserByUserName()方法自定义UserDetailServiceImpl继承UserDetailService接口重写其loadUserByUserName()方法查到用户后封装成UserDetail对象返回
4、调用passwordEncoder的验证方法进行用户信息的认证(一般使用BCryptPasswordEncoder创建此Bean并放入容器中)
5、认证通过后使用JWT创建token并放到redis中返回token到前端
携带token访问
1、请求到达自定义Filter中获取token先验证token的正确性从token中获取到userId根据userId从redis中获取用户信息将用户信息封装成Authentication对象放进SecurityContextHolder中后面的过滤器会在SecurityContextHolder中获取用户的信息
2、请求到达FilterSecurityInterceptor在springSecurity中默认使用FilterSecurityInterceptor来进行权限校验FilterSecurityInterceptor会从SecurityContextHolder中获取Authentication然后获取其中的权限信息判断当前用户是否拥有当前资源的访问权限
3、通过权限判断获取资源返回给前端
登录认证流程图 基于RBAC的授权控制
RBAC概念
Role-Based Access Control中文意思是基于角色Role的访问控制。这是一种广泛应用于计算机系统和网络安全领域的访问控制模型。
简单来说就是通过将权限分配给角色再将角色分配给用户来实现对系统资源的访问控制。一个用户拥有若干角色每一个角色拥有若干权限。这样就构造成“用户-角色-权限”的授权模型。在这种模型中用户与角色之间角色与权限之间一般是多对多的关系
模型 在数据库中主要体现用户表、角色表、菜单表、用户角色关联表、角色菜单关联表五个模型
CREATE TABLE mySchema.t_user
(
id INT IDENTITY(1, 1) NOT NULL,
username VARCHAR(100) NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL,
phone VARCHAR(100),
UNIQUE(id),
UNIQUE(username),
UNIQUE(email),
UNIQUE(phone),
NOT CLUSTER PRIMARY KEY(id)) STORAGE(ON MAIN, CLUSTERBTR) ;COMMENT ON TABLE mySchema.t_user IS 用户表;
COMMENT ON COLUMN mySchema.t_user.id IS 用户id;
COMMENT ON COLUMN mySchema.t_user.username IS 用户名;
COMMENT ON COLUMN mySchema.t_user.password IS 密码;
COMMENT ON COLUMN mySchema.t_user.email IS 用户邮箱;
COMMENT ON COLUMN mySchema.t_user.phone IS 电话;CREATE TABLE mySchema.sys_role
(
id BIGINT IDENTITY(1, 1) NOT NULL,
name VARCHAR(128) DEFAULT NULL,
role_key VARCHAR(100) DEFAULT NULL,
status CHAR(1) DEFAULT 0,
del_flag INT DEFAULT 0,
create_by BIGINT DEFAULT NULL,
create_time DATETIME(6) DEFAULT NULL,
update_by BIGINT DEFAULT NULL,
update_time DATETIME(6) DEFAULT NULL,
remark VARCHAR(500) DEFAULT NULL,
NOT CLUSTER PRIMARY KEY(id)) STORAGE(ON MAIN, CLUSTERBTR) ;COMMENT ON TABLE mySchema.sys_role IS 角色表;
COMMENT ON COLUMN mySchema.sys_role.id IS 角色id;
COMMENT ON COLUMN mySchema.sys_role.name IS 角色名称;
COMMENT ON COLUMN mySchema.sys_role.role_key IS 角色权限字符串;
COMMENT ON COLUMN mySchema.sys_role.status IS 角色状态(0正常, 1停用);
COMMENT ON COLUMN mySchema.sys_role.del_flag IS 删除标志(0未删除1已删除);
COMMENT ON COLUMN mySchema.sys_role.remark IS 备注;CREATE TABLE mySchema.sys_menu
(
id BIGINT IDENTITY(2, 1) NOT NULL,
menu_name VARCHAR(64) DEFAULT NULL NOT NULL,
path VARCHAR(200) DEFAULT NULL,
component VARCHAR(50) DEFAULT NULL,
visible CHAR(1) DEFAULT 0,
status CHAR(1) DEFAULT 0,
perms VARCHAR(100) DEFAULT NULL,
icon VARCHAR(100) DEFAULT #,
create_by BIGINT DEFAULT NULL,
create_time DATETIME(6) DEFAULT CURRENT_TIMESTAMP,
update_by BIGINT DEFAULT NULL,
update_time DATETIME(6) DEFAULT CURRENT_TIMESTAMP,
delete_f1ag INT DEFAULT 0,
remark VARCHAR(500) DEFAULT NULL,
UNIQUE(id),
NOT CLUSTER PRIMARY KEY(id)) STORAGE(ON MAIN, CLUSTERBTR) ;COMMENT ON TABLE mySchema.sys_menu IS 菜单表;
COMMENT ON COLUMN mySchema.sys_menu.menu_name IS 菜单名;
COMMENT ON COLUMN mySchema.sys_menu.path IS 路由地址;
COMMENT ON COLUMN mySchema.sys_menu.component IS 组件路径;
COMMENT ON COLUMN mySchema.sys_menu.visible IS 菜单状态(0显示1隐藏);
COMMENT ON COLUMN mySchema.sys_menu.status IS 菜单状态(0正常1停用);
COMMENT ON COLUMN mySchema.sys_menu.perms IS 权限标识;
COMMENT ON COLUMN mySchema.sys_menu.icon IS 菜单图标;
COMMENT ON COLUMN mySchema.sys_menu.delete_f1ag IS 是否删除(0未删除1已删除);
COMMENT ON COLUMN mySchema.sys_menu.remark IS 备注;CREATE TABLE mySchema.sys_user_role
(
user_id BIGINT DEFAULT 0 NOT NULL,
role_id BIGINT DEFAULT 0 NOT NULL,
NOT CLUSTER PRIMARY KEY(user_id, role_id)) STORAGE(ON MAIN, CLUSTERBTR) ;COMMENT ON TABLE mySchema.sys_user_role IS 用户角色表;
COMMENT ON COLUMN mySchema.sys_user_role.user_id IS 用户id;
COMMENT ON COLUMN mySchema.sys_user_role.role_id IS 角色id;CREATE TABLE mySchema.sys_role_menu
(
role_id BIGINT DEFAULT 0 NOT NULL,
menu_id BIGINT DEFAULT 0 NOT NULL,
NOT CLUSTER PRIMARY KEY(role_id, menu_id)) STORAGE(ON MAIN, CLUSTERBTR) ;COMMENT ON TABLE mySchema.sys_role_menu IS 角色菜单关联表;
COMMENT ON COLUMN mySchema.sys_role_menu.role_id IS 角色id;
COMMENT ON COLUMN mySchema.sys_role_menu.menu_id IS 菜单id;权限流程
用户表、角色表、菜单表、部门表、用户角色关联表、角色菜单关联表数据权限分为5种个人、全部、本部门、指定部门、本部门及以下 链路
1、用户注册指定部门创建角色给角色赋菜单权限、数据权限给用户赋予角色 2、用户登录通过用户的所有角色获取所有的菜单权限和最大数据权限菜单数据直接展示在页面所有数据都有创建人则数据的部门id是创建人的部门id 3、在访问数据接口时通过mybatis的拦截器对用户的数据进行过滤(拼接sql)返回给前端
captcha图片验证码
实现思路
1、图片验证码获取并将验证码验证码存放起来(单机应用可放在session中分布式应用放在redis中)
2、当用户登录时携带验证码以及验证码的key进入到后端从redis中获取值进行验证
!-- 谷歌kaptcha验证码依赖 --
dependencygroupIdcom.github.penggle/groupIdartifactIdkaptcha/artifactIdversion2.3.2/version
/dependency代码思路
1、生成验证码
public ResultCaptchaVO getCaptcha() throws IOException {// 生成文字验证码String content defaultKaptcha.createText();// 生成图片验证码ByteArrayOutputStream outputStream null;BufferedImage image defaultKaptcha.createImage(content);outputStream new ByteArrayOutputStream();ImageIO.write(image, jpg, outputStream);// 对字节数组Base64编码String str data:image/jpeg;base64,;String base64Img str Base64.getEncoder().encodeToString(outputStream.toByteArray()).replace(\n, ).replace(\r, );CaptchaVO captchaVO captchaService.cacheCaptcha(content);captchaVO.setBase64Img(base64Img);return Result.success(captchaVO);
}2、存储验证码
Service
public class CaptchaService {private Long timeout 300L;Autowiredprivate RedisUtils redisUtils;private final String CAPTCHA_KEY_PREFIX captcha:verification:;public CaptchaVO cacheCaptcha(String captcha){//生成一个随机标识符String randomStr UUID.randomUUID().toString();//缓存验证码并设置过期时间String captchaKey CAPTCHA_KEY_PREFIX.concat(randomStr);redisUtils.set(captchaKey, captcha, timeout, TimeUnit.SECONDS);CaptchaVO captchaVO new CaptchaVO();captchaVO.setCaptchaKey(captchaKey);captchaVO.setExpire(timeout);captchaVO.setCaptcha(captcha);return captchaVO;}
}3、校验验证码
Object captcha redisUtils.get(reqVO.getCaptchaKey());
if (Objects.isNull(captcha)) {return 验证码已过期;
}
if (!captcha.equals(reqVO.getCaptcha())) {return 验证码错误;
}