文章目录
一、理论知识铺垫
1、一般性的登陆鉴权的流程

2、SpringSecurity的本质
过滤链

3、SpringSecurity的基本登陆流程原理

二、个性化SpringSecurity功能开发
1、自定义Security认证功能
自定义UserDetailsService,实现Security连接数据库,进行身份认证
- 创建user表
- 集成mybatis,实现数据库查询功能
- 创建LoginUser(它是UserDetails的实现类)
- 在UserDetailsService实现类中去查询数据库,替换默认的InMemoryUserDetailsManager
- 向SpringBoot容器中注入BCryptPasswordEncoder类
这个类的作用,不仅是认证的时候matches密码,在注册账号的时候,也要encode密码,存入数据库
2、开发登陆接口
自定义登录接口,替换默认登录页
- 创建controller接口,创建service层,在service层中调用authenticationManager.authenticate,触发UserDetailsService的查询数据库认证逻辑。
- 在SecurityConfig中,新增配置,注入AuthenticationManager,配置filterChain,放行login请求
- controller中调用service
- 生成一个随机的uuid,拼接固定的前缀,用于生成jwt和redis中的key
- 把用户信息存入redis中
- 创建jwt返回给前端
- 可以发现,用户信息没有放入token发给前端,更加安全。
3、其它接口校验
这一块,是用于用户登陆后,访问其它需要认证的接口时,携带token参数,通过token来认证用户信息的步骤。
所以,这种校验是在每个接口前就要进行的,这种时候,我们应该要想到拦截器和过滤器来实现这个功能。
security本质是一连串的过滤器组成的过滤器链,所以,这个功能放在过滤器中实现。
定义Jwt认证过滤器
- 创建过滤器JwtAuthenticationTokenFilter,继承OncePerRequestFilter
- 从request的header中获取token
- 解析token获取其中userKey
- 通过userKey,从Redis中获取用户信息
- 验证登陆的loginUser是否超时,并刷新Redis数据
- 创建authenticationToken,存入SecurityContextHolder中
4、退出登陆
清除Redis中的token,清除SecurityContextHolder中的用户认证信息
注意,这块无需开发controller接口,直接在SecurityConfig增加一个退出的请求地址即可。
- 修改SecurityConfig配置,指定logout请求地址以及LogoutSuccessHandler,负责退出具体操作。
handler的方式,就要额外处理一下给浏览器的数据写回。
相对来讲,写controller接口,貌似方便一些。
5、认证功能相关表
1~4步骤的开发都是关于security的认证功能,涉及到的表就一个sys_user表即可。
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID',
`user_name` varchar(30) NOT NULL COMMENT '用户账号',
`nick_name` varchar(30) NOT NULL COMMENT '用户昵称',
`user_type` varchar(2) DEFAULT '00' COMMENT '用户类型(00系统用户)',
`email` varchar(50) DEFAULT '' COMMENT '用户邮箱',
`phonenumber` varchar(11) DEFAULT '' COMMENT '手机号码',
`sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
`avatar` varchar(100) DEFAULT '' COMMENT '头像地址',
`password` varchar(100) DEFAULT '' COMMENT '密码',
`status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
`login_ip` varchar(128) DEFAULT '' COMMENT '最后登录IP',
`login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='用户信息表';
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', '103', 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2025-01-20 15:57:58', 'admin', '2024-12-16 10:35:31', '', '2025-01-20 15:57:58', '管理员');
INSERT INTO `sys_user` VALUES ('2', '105', 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2025-01-11 09:46:43', 'admin', '2024-12-16 10:35:31', '', '2025-01-11 09:46:43', '测试员');
INSERT INTO `sys_user` VALUES ('5', '105', 'test', '测试', '00', '', '', '0', '', '$2a$10$B6aytqjmXyw10PEDdwTjC.S0UT/dGjdMIsKqiVzMJ.ktP.iMgMQ5G', '0', '0', '', null, 'ry', '2024-12-20 19:29:40', '', null, null);
6、鉴权功能
定义:不同的用户,使用的功能不同,对数据的操作权限不同。
一般有两部分
1、前端根据用户权限,隐藏菜单或按钮。
2、后端的接口权限控制,数据权限控制。
1、功能设计
一般的后台管理系统,都分为这几个部分:目录、菜单、按钮
目录:就是指包含自菜单的菜单,可以展开的菜单,点击菜单,主页面不会切换。
菜单:菜单就是没有子菜单的菜单,点击菜单,主页面会切换。
按钮:页面上的增删改查、导出、上传等按钮
可以根据自己业务的需求,进行适当的扩展。
2、表设计
用户表:sys_user
部门表:sys_dept
角色表:sys_role
用户角色表:sys_user_role
角色权限表:sys_role_menu
权限表:sys_menu,权限表相对来讲设计最复杂。
因为,菜单之间存在父子关系,所以,要设计parent_id,保存每条数据的关联关系。
然后,菜单权限,我这里是设计成3层,目录,菜单,按钮


RBAC:是基于角色的权限设计模型
所以,我们可以用用户直接关联角色,也可以用部门关联角色
根据自己的需要,进行相关设计。
这块的查询逻辑要分三块:
1、admin角色,直接返回所有权限,不用查询数据库。
2、没有绑定角色的user,直接根据userId查权限。
3、绑定多个roleId的user,根据roleIds,循环遍历查询权限。
mybatis实现表与表之间一对一和一对多的关系,用association和collection实现。
如下:
SysUser和SysDept是一对一
SysUser和roles是一对多
<resultMap type="SysUser" id="SysUserResult">
<id property="userId" column="user_id" />
<association property="dept" javaType="SysDept" resultMap="deptResult" />
<collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
</resultMap>

3、Security实现
1、从数据库查询出菜单权限数据,存入用户信息中。
2、通过@EnableMethodSecurity+@PreAuthorize注解给接口标识所需权限,在用户请求接口时,比对权限。
spring-security-config.jar
5.5.x用:@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
5.7.x用:@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
7、自定义失败处理
主要包括两部分:认证失败处理,授权失败处理
在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationfilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。
如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler然后配置给SpringSecurity即可。
8、跨域配置
实现WebMvcConfigurer接口,并注入一个CorsFilter即可。
924

被折叠的 条评论
为什么被折叠?



