使用cookie和session实现用户的认证和授权(原生方式,不使用安全框架)

在学习springsecurity和shiro等安全框架之前可以使用原生的方式,基于cookie和session实现原生的认证,有利于加强对框架学习的理解,也有助于清晰的理解认证和授权的流程,也有助于对cookie和session作用的理解。
对cookie和session的概念不清楚的可以参考:cookie和session
一般的管理系统都是基于RABC授权,也即基于角色授权,RABC授权模式最简单的就是利用5张表:用户表,角色表,权限表,用户角色中间表,角色权限中间表,这里就基于这5张表进行讲解。

案例代码和SQL放在gitte中案例源码地址

/*
 Navicat Premium Data Transfer

 Source Server         : 本地
 Source Server Type    : MySQL
 Source Server Version : 80025
 Source Host           : localhost:3306
 Source Schema         : sys_cookie

 Target Server Type    : MySQL
 Target Server Version : 80025
 File Encoding         : 65001

 Date: 23/03/2022 16:30:57
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id,自增',
  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '访问的url',
  `permission_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '权限许可码',
  `permission_note` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '权限描述',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES (1, '/selectUser', 'select_user', '查看用户信息');
INSERT INTO `sys_permission` VALUES (2, '/addUser', 'add_user', '添加用户信息');
INSERT INTO `sys_permission` VALUES (3, '/updateUser', 'update_user', '更新用户信息');
INSERT INTO `sys_permission` VALUES (4, '/deleteUser', 'delete_user', '删除用户信息');
INSERT INTO `sys_permission` VALUES (5, '/selectLogs', 'select_logs', '查看系统日志');
INSERT INTO `sys_permission` VALUES (6, '/selectFinance', 'select_finance', '查看财务信息');

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '角色id,主键自增',
  `role_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '角色名称',
  `role_note` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '角色拥有的权限',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, '系统超级管理员', '拥有系统所有权限');
INSERT INTO `sys_role` VALUES (2, '人事经理', '拥有用户所有管理权限');
INSERT INTO `sys_role` VALUES (3, '总经理', '拥有系统部分权限');
INSERT INTO `sys_role` VALUES (4, '财务总监', '拥有财务管理权限');

-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_id` int NULL DEFAULT NULL,
  `permission_id` int NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES (1, 1, 1);
INSERT INTO `sys_role_permission` VALUES (2, 1, 2);
INSERT INTO `sys_role_permission` VALUES (3, 1, 3);
INSERT INTO `sys_role_permission` VALUES (4, 1, 4);
INSERT INTO `sys_role_permission` VALUES (5, 1, 5);
INSERT INTO `sys_role_permission` VALUES (6, 1, 6);
INSERT INTO `sys_role_permission` VALUES (7, 2, 1);
INSERT INTO `sys_role_permission` VALUES (8, 2, 2);
INSERT INTO `sys_role_permission` VALUES (9, 2, 3);
INSERT INTO `sys_role_permission` VALUES (10, 2, 4);
INSERT INTO `sys_role_permission` VALUES (11, 3, 1);
INSERT INTO `sys_role_permission` VALUES (12, 3, 5);
INSERT INTO `sys_role_permission` VALUES (13, 3, 6);
INSERT INTO `sys_role_permission` VALUES (14, 4, 6);

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '用户id,主键自增',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码',
  `fullname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '真实姓名',
  `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '电话',
  `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'zhangsan', '123456', '张三', '15070443883', '2021-09-18 14:27:56', '2021-09-18 14:28:05');
INSERT INTO `sys_user` VALUES (2, 'lisi', '321123', '李四', '13496858978', '2021-09-18 14:29:11', '2021-09-20 14:29:14');
INSERT INTO `sys_user` VALUES (3, 'wangwu', '654321', '王五', '18965458769', '2021-09-18 16:06:35', '2021-09-18 16:06:41');

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id,自增',
  `user_id` int NULL DEFAULT NULL COMMENT '用户id',
  `role_id` int NULL DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (1, 1, 1);
INSERT INTO `sys_user_role` VALUES (2, 2, 2);
INSERT INTO `sys_user_role` VALUES (3, 2, 3);
INSERT INTO `sys_user_role` VALUES (4, 3, 4);

SET FOREIGN_KEY_CHECKS = 1;

一.认证

认证实体:首先创建一个认证实体,用户实体,角色实体,权限实体
认证实体

@Data
public class AuthenticationRequest {

    private String username;
    private String password;

}

用户实体

@Data
public class SysUser {

    public static final String USER_SESSION_KEY = "user_session_key";

    private Integer id;
    private String username;
    private String password;
    private String fullname;
    private String phone;
    private Date createdTime;
    private Date updatedTime;
    private List<SysRole> roles;
    private List<SysPermission> sysPermissions;

}

角色实体

@Data
public class SysRole {

    private Integer id;
    private String roleName;
    private String roleNote;

}

权限实体

@Data
public class SysPermission {

    private Integer id;
    private String url;
    private String permissionCode;
    private String permissionNote;

}

认证控制器:在controller中有一个登入控制器,传入两个参数:认证实体和HttpServletRequest,这里首先调认证业务进行认证处理,认证成功后则通过HttpServletRequest获取session对象,然后将用户的基本信息(用户信息,用户拥有的角色,用户拥有的权限)存入session中,并且设置好session的过期时间。

@RestController
public class UserController {

    @Autowired
    private AuthenticationService authenticationService;

    @PostMapping("/login")
    public JsonResult login(AuthenticationRequest authenticationRequest, HttpServletRequest httpServletRequest){
        SysUser sysUser = authenticationService.authentication(authenticationRequest);
        //获取session对象
        HttpSession session = httpServletRequest.getSession();
        //将用户的信息放入到session中
        session.setAttribute(SysUser.USER_SESSION_KEY, sysUser);
        //设置session失效时间,单位为秒
        session.setMaxInactiveInterval(60);
        return new JsonResult("登入成功", sysUser);
    }

}

认证业务类:认证业务类中首先对用户名做非空判断,不为空后再根据用户输入的用户名去查询数据库,查询出用户的基本信息,取出密码和用户输入的密码比对,密码相匹配后再根据用户的id去关联5张表,查询出用户的信息,用户的角色以及用户的权限。

@Service
public class AuthenticationServiceImpl implements AuthenticationService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public SysUser authentication(AuthenticationRequest authenticationRequest) {
        String username = authenticationRequest.getUsername();
        //先对用户名做非空判断
        if (StringUtils.isBlank(username)){
            throw new RuntimeException("用户名不能为空");
        }
        //不为空则根据用户名到数据库中查找用户信息
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .select("id", "username", "password", "password", "fullname", "phone")
                .eq("username", username);
        SysUser sysUser = sysUserMapper.selectOne(queryWrapper);
        //判断数据库是否存在该用户
        if (sysUser == null){
            throw new RuntimeException("系统不存在该用户名,请先注册");
        }
        //存在则根据用户输入的密码和数据库取出的用户密码进行比对
        if (!sysUser.getPassword().equals(authenticationRequest.getPassword())){
            throw new RuntimeException("密码错误,请重试");
        }
        //密码匹配后根据用户id到数据库中查出用户的基本信息(用户信息,用户的角色,用户的权限)
        sysUser = sysUserMapper.selectSysUserInfo(sysUser.getId());
        return sysUser;
    }

}

认证数据层

@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {

    public SysUser selectSysUserInfo(Integer id);

}

用户数据层的映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bingqin.sys.dao.SysUserMapper">

    <select id="selectSysUserInfo" resultType="sysUser" resultMap="userInfoMap">
        SELECT
            u.username,
            u.fullname,
            u.phone,
            r.role_name,
            r.role_note,
            p.url,
            p.permission_code,
            p.permission_note
        FROM
            sys_user u
                INNER JOIN sys_user_role ur ON u.id = ur.user_id
                INNER JOIN sys_role r ON r.id = ur.role_id
                INNER JOIN sys_role_permission rp ON r.id = rp.role_id
                INNER JOIN sys_permission p ON p.id = rp.permission_id
        WHERE
            u.id = #{id}
    </select>

    <resultMap id="userInfoMap" type="sysUser">
        <result property="username" column="username"/>
        <result property="fullname" column="fullname"/>
        <result property="phone" column="phone"/>
        <collection property="roles" ofType="sysRole">
            <result property="roleName" column="role_name"/>
            <result property="roleNote" column="role_note"/>
        </collection>
        <collection property="sysPermissions" ofType="sysPermission">
            <result property="url" column="url"/>
            <result property="permissionCode" column="permission_code"/>
            <result property="permissionNote" column="permission_note"/>
        </collection>
    </resultMap>


</mapper>

登出:认证做完后下面介绍登出功能,用户退出系统后台只需将用户的session信息移除即可。

@PostMapping("/logout")
    public JsonResult logout(HttpServletRequest httpServletRequest){
        HttpSession session = httpServletRequest.getSession();
        session.removeAttribute(SysUser.USER_SESSION_KEY);
        return new JsonResult("登出成功");
    }

二.鉴权

用户登入后需要访问其他接口,但是如何判断用户是否有该权限,这里需在后台进行判断,判断的方式就是每当用户访问一个接口时都会先执行拦截器,在拦截器中对用户的权限进行鉴别,如果该用户有该url的权限则对其放行,没有权限的则告知用户无权限访问。
首先定义一个拦截器,实现preHandle方法

@Component
public class AuthorizeInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        //根据session的键取出用户的信息
        SysUser sysUserInfo = (SysUser) session.getAttribute(SysUser.USER_SESSION_KEY);
        if (sysUserInfo == null){
            throw new RuntimeException("请先登入");
        }
        //拿到用户访问的uri
        String uri = request.getRequestURI();
        //取出用户权限
        List<SysPermission> userPermissions = sysUserInfo.getSysPermissions();
        for (SysPermission userPermission : userPermissions) {
            if (userPermission.getUrl().contains(uri)){
                //权限匹配则放行
                return true;
            }
        }
        //无权则抛异常
        throw new RuntimeException("无权限访问该接口,请联系管理员");
    }
}

然后定义一个配置类,实现HandlerInterceptor,并将自己定义的拦截器添加进去,这里开放登入和登出接口,/login,/logout,其他接口都做拦截处理。

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AuthorizeInterceptor authorizeInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authorizeInterceptor)
                .addPathPatterns("/**").excludePathPatterns("/login", "/logout");
        WebMvcConfigurer.super.addInterceptors(registry);
    }

}

三.测试

这里以wangwu这个用户做测试,wangwu的角色是财务总监,只拥有财务管理权限,其他的接口都不允许访问。

3.1登入

在这里插入图片描述

3.2接口访问测试

首先访问查看财务信息接口
在这里插入图片描述
再访问wangwu不拥有的权限,如:/selectUser
在这里插入图片描述

3.3登出

在这里插入图片描述
登出后不能访问其他接口,需要再次登入。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值