测姓名网站(一套简单的登录)

  前言

  无论是SpringSecruity、Shiro,对于一些小项目来说都太过复杂,有些情况下我们就想使用简单的登录、鉴权功能,本文记录手写一套简单的登录、鉴权工具

  思路

  1、封装工具类,集成查询系统用户、系统角色,根据登录用户权限进行当前URL请求鉴权

  2、在拦截器中调用工具类进行鉴权,通过放行、不通过则抛出对应业务异常信息

  首先需要三张基础表:系统用户表、系统角色表、用户角色关联表

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '表id',
  `nick_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称',
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '账号',
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '系统用户表' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', '系统管理员', 'admin', '000000');
INSERT INTO `sys_user` VALUES ('2', '张三-部门经理', 'zhangsan', '111111');
INSERT INTO `sys_user` VALUES ('3', '小芳-前台接待', 'xiaofang', '222222');

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '表id',
  `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名称',
  `role_menu` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '角色菜单可视权限(可以不关联菜单,单独做成菜单管理直接与用户关联)',
  `role_url` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '角色URL访问权限',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '系统角色表' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', '管理员', '[{\"menuName\":\"系统管理\",\"menuPath\":\"/sys/xtgl\"},{\"menuName\":\"用户管理\",\"menuPath\":\"/sys/yhgl\"},{\"menuName\":\"网站门户管理\",\"menuPath\":\"/portal/mhgl\"}]', '/sys/*,/portal/mhgl,/getLoginUser');
INSERT INTO `sys_role` VALUES ('2', '部门领导', '[{\"menuName\":\"用户管理\",\"menuPath\":\"/sys/yhgl\"},{\"menuName\":\"网站门户管理\",\"menuPath\":\"/portal/mhgl\"}]', '/sys/yhgl,/portal/mhgl,/getLoginUser');
INSERT INTO `sys_role` VALUES ('3', '普通员工', '[{\"menuName\":\"网站门户管理\",\"menuPath\":\"/portal/mhgl\"}]', '/portal/mhgl,/getLoginUser');

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '表id',
  `user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户id',
  `role_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '系统用户-角色关联表' ROW_FORMAT = Compact;

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

  在工具类中定义三个实体类方便传参接参(如果嫌麻烦也可以直接使用Map对象),使用自定义DbUtil查询数据库表数据(此操作,应交由项目ORM框架负责)

  代码编写

  DbUtil工具类

package cn.huanzi.qch.util;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * 原生jdbc操作数据库工具类
 */
public class DbUtil {

    //数据库连接:地址、用户名、密码
    private final String url;
    private final String username;
    private final String password;

    //Connection连接实例
    private Connection connection;

    public DbUtil(String url, String username, String password){
        this.url = url;
        this.username = username;
        this.password = password;
    }
    public DbUtil(String url, String username, String password, String driver){
        this(url,username,password);

        //加载驱动
        try {
            /*
                同时需要引入相关驱动依赖

                1、MySQL:
                com.mysql.cj.jdbc.Driver

                2、Oracle:
                oracle.jdbc.driver.OracleDriver

                3、pgsql:
                org.postgresql.Driver

             */
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取 Connection 连接
     */
    private Connection getConnection() {
        if(connection == null){
            try {
                connection= DriverManager.getConnection(url, username, password);
                connection.setAutoCommit(true);
            } catch (SQLException e) {
                System.err.println("获取Connection连接异常...");
                e.printStackTrace();
            }
        }
        return connection;
    }

    /**
     * 设置是否自动提交事务
     * 当需要进行批量带事务的操作时,关闭自动提交手动管理事务,将会大大提高效率!
     */
    public void setAutoCommit(boolean autoCommit){
        try {
            this.getConnection().setAutoCommit(autoCommit);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭自动提交事务时,需要手动管理事务提交、回滚
     */
    public void commit(){
        try {
            this.getConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public void rollback(){
        try {
            this.getConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭 Connection 连接
     */
    public void close(){
        if(connection != null){
            try {
                connection.close();
                connection = null;
            } catch (SQLException e) {
                System.err.println("关闭Connection连接异常...");
                e.printStackTrace();
            }
        }
    }

    /**
     * 查询
     * 查询语句
     */
    public ArrayList<HashMap<String,Object>> find(String sql, Object[] params) {
        ArrayList<HashMap<String, Object>> list = new ArrayList<>();

        //获取连接
        Connection conn = this.getConnection();
        PreparedStatement ps;
        ResultSet rs;

        try {
            //设置SQL、以及参数
            ps = conn.prepareStatement(sql);
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }

            //执行查询
            rs = ps.executeQuery();

            //获取查询结果
            ResultSetMetaData rm = rs.getMetaData();
            int columnCount = rm.getColumnCount();

            //封装结果集
            while (rs.next()) {
                HashMap<String, Object> map = new HashMap<>(columnCount);
                for (int i = 1; i <= columnCount; i++) {
                    String name = rm.getColumnName(i).toLowerCase();
                    Object value = rs.getObject(i);

                    map.put(name,value);
                }
                list.add(map);
            }

        } catch (Exception e) {
            System.err.println("执行 jdbcUtil.find() 异常...");
            e.printStackTrace();
        }

        return list;
    }
    public HashMap<String,Object> findOne(String sql, Object[] params){
        ArrayList<HashMap<String, Object>> list = this.find(sql, params);
        return list.size() > 0 ? list.get(0) : null;
    }
    public ArrayList<HashMap<String,Object>> find(String sql) {
        return this.find(sql,null);
    }
    public HashMap<String,Object> findOne(String sql) {
        return this.findOne(sql,null);
    }

    /**
     * 执行
     * 新增/删除/更新 等SQL语句
     */
    public boolean execute(String sql, Object[] params){
        boolean flag = false;

        //获取连接
        Connection conn = this.getConnection();
        PreparedStatement ps;

        try {
            //设置SQL、以及参数
            ps = conn.prepareStatement(sql);
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }

            //执行
            flag = ps.execute();
        } catch (SQLException e) {
            System.err.println("执行 jdbcUtil.update() 异常...");
            e.printStackTrace();
        }

        return flag;
    }
    public boolean execute(String sql){
        return this.execute(sql,null);
    }
}

  SecurityUtil工具类

package cn.huanzi.qch.util;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * 一套简单的登录、鉴权工具
 */
public class SecurityUtil {

    /**
     * 单例模式-饿汉
     */
    private static final SecurityUtil instance = new SecurityUtil();
    private SecurityUtil (){}
    public static SecurityUtil getInstance() {
        return instance;
    }

    /**
     * 无需登录即可访问的URL
     * PS:建议从配置文件读取
     */
    private static final String[] URLS = {
            //登录页、登录请求、注销请求
            "/loginPage",
            "/login",
            "/logout",

            //静态资源,例如:js、css等
            "/assets/**",

            //一些特殊无需权限控制的地址、api
            "/portal/index",
    };

    /**
     * 用户角色信息一般情况下是不轻易更改,可以将结果存储到缓存对象
     */
    private static HashMap<String,List<Role>> userRoleMap = new HashMap<>(10);

    //查询数据库操作,应交由项目ORM框架负责
    private final DbUtil dbUtil = new DbUtil("jdbc:mysql://localhost/jfinal_demo","root","123456");

    /**
     * 鉴权中心
     * PS:返回值类型有待商榷
     */
    public String auc(HttpServletRequest request){
        //请求URL地址
        String requestUri = request.getRequestURI();

        SecurityUtil securityUtil = SecurityUtil.getInstance();

        //是否为无需登录即可访问URL
        if(SecurityUtil.checkUrl(requestUri,SecurityUtil.URLS)){
            //允许访问!
            return "SUCCEED";
        }

        //是否为登录用户
        SecurityUtil.User loginUser = securityUtil.getLoginUser(request);
        if(loginUser == null){
            //未登录或登录凭证过期!
            return "UNAUTHORIZED";
        }

        //该登录用户是否有权访问当前URL
        if(!SecurityUtil.checkUrl(requestUri,securityUtil.getRoleUrlByUserId(loginUser.getId()))){
            //抱歉,你无权限访问!
            return "FORBIDDEN";
        }

        //允许访问!
        return "SUCCEED";
    }

    /**
     * 检查requestUri是否包含在urls中
     */
    public static boolean checkUrl(String requestUri,String[] urls){
        //对/进行特殊处理
        if("/".equals(requestUri) && !Arrays.asList(urls).contains(requestUri)){
            return false;
        }

        String[] requestUris = requestUri.split("/");
        for (String url : urls) {
            if (check(requestUris, url.split("/"))) {
                return true;
            }
        }

        return false;
    }
    private static boolean check(String[] requestUris,String[] urls){
        for (int i1 = 0; i1 < requestUris.length; i1++) {
            //判断长度
            if (i1 >= urls.length){
                return false;
            }

            //处理/*、/**情况
            if("**".equals(urls[i1])){
                return true;
            }
            if("*".equals(urls[i1])){
                continue;
            }

            //处理带后缀
            if(requestUris[i1].contains(".") && urls[i1].contains(".")){
                String[] split = requestUris[i1].split("\\.");
                String[] split2 = urls[i1].split("\\.");

                // *.后缀的情况
                if("*".equals(split2[0]) && split[1].equals(split2[1])){
                    return true;
                }
            }

            //不相等
            if(!requestUris[i1].equals(urls[i1])){
                return false;
            }

        }

        return true;
    }

    /**
     * 从request设置、获取当前登录用户
     * PS:登录用户可以放在session中,也可以做做成jwt
     */
    public void setLoginUser(HttpServletRequest request,User loginUser){
        request.getSession().setAttribute("loginUser",loginUser);
    }
    public User getLoginUser(HttpServletRequest request){
        return (User)request.getSession().getAttribute("loginUser");
    }
    public List<Role> getLoginUserRole(HttpServletRequest request){
        User loginUser = this.getLoginUser(request);
        return loginUser != null ? getRoleByUserId(loginUser.getId()) : null;
    }

    /**
     * 根据用户id,获取用户允许访问URL
     */
    public String[] getRoleUrlByUserId(String userId){
        StringBuilder roleUrl = new StringBuilder();
        for (SecurityUtil.Role role : this.getRoleByUserId(userId)) {
            roleUrl.append(",").append(role.getRoleUrl());
        }
        return roleUrl.toString().split(",");
    }

    /**
     * 获取用户、用户角色
     * PS:这些查询数据库操作,应交由项目ORM框架负责
     */
    public User getUserByUserNameAndPassword(String username,String password){
        //PS:密码应该MD5加密后密文存储,匹配时先MD5加密后匹配,本例中存储的是明文,就不进行MD5加密了
        User user = null;
        HashMap<String, Object> map = dbUtil.findOne("select * from sys_user where user_name = ? and password = ?", new String[]{username, password});
        if(map != null){
            user = new User(map.get("id").toString(),map.get("nick_name").toString(),map.get("user_name").toString(),map.get("password").toString());
        }

        //关闭数据库连接
        dbUtil.close();

        return user;
    }
    public List<Role> getRoleByUserId(String userId){
        //先从缓存中获取
        List<Role> roles = userRoleMap.get(userId);
        if(roles != null){
            return roles;
        }

        //查询数据库
        List<Role> roleList = null;
        List<HashMap<String, Object>> list = dbUtil.find("select r.* from sys_role r join sys_user_role ur on r.id = ur.role_id where ur.user_id = ?", new String[]{userId});
        if(list != null){
            roleList = new ArrayList<>(list.size());
            for (HashMap<String, Object> map : list) {
                roleList.add(new Role(map.get("id").toString(),map.get("role_name").toString(),map.get("role_menu").toString(),map.get("role_url").toString()));
            }
        }

        //关闭数据库连接
        dbUtil.close();

        //放到缓存中
        userRoleMap.put(userId,roleList);

        return roleList;
    }

    /*
        3张基础表

        sys_user 系统用户表
            id        表id
            nick_name 昵称
            user_name 账号
            password  密码

        sys_role 系统角色表
            id        表id
            role_name 角色名称
            role_menu 角色菜单可视权限(可以不关联菜单,单独做成菜单管理直接与用户关联)
            role_url  角色URL访问权限

        sys_user_role 系统用户-角色关联表
            id      表id
            user_id 用户id
            role_id 角色id
     */
    public class User{
        private String id;//表id
        private String nickName;//昵称
        private String userName;//账号
        private String password;//密码

        public User(String id, String nickName, String userName, String password) {
            this.id = id;
            this.nickName = nickName;
            this.userName = userName;
            this.password = password;
        }

        public String getId() {
            return id;
        }

        public String getNickName() {
            return nickName;
        }

        public String getUserName() {
            return userName;
        }

        public String getPassword() {
            return password;
        }
    }
    public class Role{
        private String id;//表id
        private String RoleName;//角色名称
        private String RoleMenu;//角色菜单可视权限(可以不关联菜单,单独做成菜单管理直接与用户关联)
        private String RoleUrl;//角色URL访问权限

        public Role(String id, String roleName, String roleMenu, String roleUrl) {
            this.id = id;
            RoleName = roleName;
            RoleMenu = roleMenu;
            RoleUrl = roleUrl;
        }

        public String getId() {
            return id;
        }

        public String getRoleName() {
            return RoleName;
        }

        public String getRoleMenu() {
            return RoleMenu;
        }

        public String getRoleUrl() {
            return RoleUrl;
        }
    }
    public class UserRole{
        private String id;//表id
        private String UserId;//用户id
        private String RoleId;//角色id
    }
}

  数据库目前用的是mysql,使用时要记得添加驱动依赖

  SpringBoot整合

  代码

  PS:我们自定义DbUtil工具类获取连接操作,SpringBoot项目需要带上时区、字符集参数

jdbc:mysql://localhost/jfinal_demo?serverTimezone=GMT%2B8&characterEncoding=utf-8

  新建一个springboot项目或在我们的springBoot项目中随便挑一个来测试

  首先需要将springboot-exceptionhandler项目中自定义统一异常处理相关代码拷贝过来,方便捕获我们抛出的业务异常

  然后新建一个AccessAuthorityFilter拦截器

/**
 * SpringBoot测试鉴权拦截器
 */
@WebFilter(filterName = "AccessAuthorityFilter",urlPatterns = {"/**"})
@ServletComponentScan
@Component
public class AccessAuthorityFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //请求头
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        SecurityUtil securityUtil = SecurityUtil.getInstance();

        //鉴权中心
        String auc = securityUtil.auc(request);
        if("UNAUTHORIZED".equals(auc)){
            throw new ServiceException(ErrorEnum.UNAUTHORIZED);
        }
        if("FORBIDDEN".equals(auc)){
            throw new ServiceException(ErrorEnum.FORBIDDEN);
        }

        //执行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

  写几个测试接口,包括login登录、logout注销等

/**
 * 测试接口
 */
@RestController
public class TestController {

    /**
     * 简单登录、注销、获取登录用户
     */
    @GetMapping("/login")
    public String login(HttpServletRequest request,String username, String password){
        SecurityUtil securityUtil = SecurityUtil.getInstance();
        SecurityUtil.User user = securityUtil.getUserByUserNameAndPassword(username, password);
        if(user != null){
            securityUtil.setLoginUser(request,user);
            return "登录成功!";
        }else{
            return "账号或密码错误...";
        }
    }
    @GetMapping("/logout")
    public String logout(HttpServletRequest request){
        SecurityUtil securityUtil = SecurityUtil.getInstance();
        SecurityUtil.User loginUser = securityUtil.getLoginUser(request);
        securityUtil.setLoginUser(request,null);

        return "注销成功!";
    }
    @GetMapping("/getLoginUser")
    public HashMap<String, Object> getLoginUser(HttpServletRequest request){
        SecurityUtil securityUtil = SecurityUtil.getInstance();
        SecurityUtil.User loginUser = securityUtil.getLoginUser(request);
        List<SecurityUtil.Role> loginUserRole = securityUtil.getLoginUserRole(request);

        HashMap<String, Object> map = new HashMap<>(2);
        map.put("loginUser",loginUser);
        map.put("loginUserRole",loginUserRole);
        return map;
    }

    /**
     * 登录、鉴权测试接口
     */
    @GetMapping("/sys/xtgl")
    public String xtgl() {
        return "系统管理...";
    }
    @GetMapping("/sys/yhgl")
    public String yhgl() {
        return "用户管理...";
    }
    @GetMapping("/portal/mhgl")
    public String mhgl() {
        return "网站门户管理...";
    }
    @GetMapping("/portal/index")
    public String portalIndex() {
        return "网站门户首页...";
    }
}

  效果

   未登录时,只有配置在无需登录即可访问的URL才能允许访问

  登录后,除了无需权限的URL,还可以访问角色允许访问的URL,注销后恢复登录前状态

  SpringBoot项目比较常规大家用的也比较多,代码就不上传了

  JFinal整合

  代码

  创建一个访问权限拦截器AccessAuthorityInterceptor

package cn.huanzi.qch.interceptor;

import cn.huanzi.qch.common.model.ErrorEnum;
import cn.huanzi.qch.common.model.ServiceException;
import cn.huanzi.qch.util.SecurityUtil;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.log.Log;

import javax.servlet.http.HttpServletRequest;

/**
 * 访问权限拦截器
 */
public class AccessAuthorityInterceptor implements Interceptor {
    private static final Log log = Log.getLog(AccessAuthorityInterceptor.class);

    @Override
    public void intercept(Invocation invocation) {
        //请求头
        HttpServletRequest request = invocation.getController().getRequest();

        SecurityUtil securityUtil = SecurityUtil.getInstance();

        //鉴权中心
        String auc = securityUtil.auc(request);
        if("UNAUTHORIZED".equals(auc)){
            throw new ServiceException(ErrorEnum.UNAUTHORIZED);
        }
        if("FORBIDDEN".equals(auc)){
            throw new ServiceException(ErrorEnum.FORBIDDEN);
        }

        invocation.invoke();
    }
}

  AppConfig中注册拦截器

/**
 * API 引导式配置
 */
public class AppConfig extends JFinalConfig {

    //省略其他代码...
    
    /**
     * 配置路由
     */
    public void configRoute(Routes me) {
        //省略其他代码...

        // 此处配置 Routes 级别的拦截器,可配置多个
        me.addInterceptor(new AccessAuthorityInterceptor());
    }
    
    //省略其他代码...
}

  写几个测试接口,包括login登录、logout注销等

/**
 * 用户表 Controller
 *
 * 作者:Auto Generator By 'huanzi-qch'
 * 生成日期:2021-07-29 17:32:50
 */
@Path(value = "/user",viewPath = "/user")
public class UserController extends CommonController<User,UserServiceImpl> {
    //省略其他代码...

    /**
     * 简单登录、注销、获取登录用户
     */
    @ActionKey("/login")
    public void login() {
        String username = get("username");
        String password = get("password");

        SecurityUtil securityUtil = SecurityUtil.getInstance();
        SecurityUtil.User user = securityUtil.getUserByUserNameAndPassword(username, password);
        if(user != null){
            securityUtil.setLoginUser(this.getRequest(),user);
            renderText("登录成功!");
        }else{
            renderText("账号或密码错误...");
        }
    }
    @ActionKey("/logout")
    public void logout() {
        SecurityUtil securityUtil = SecurityUtil.getInstance();
        SecurityUtil.User loginUser = securityUtil.getLoginUser(this.getRequest());
        securityUtil.setLoginUser(this.getRequest(),null);

        renderText("注销成功!");
    }
    @ActionKey("/getLoginUser")
    public void getLoginUser() {
        SecurityUtil securityUtil = SecurityUtil.getInstance();
        SecurityUtil.User loginUser = securityUtil.getLoginUser(this.getRequest());
        List<SecurityUtil.Role> loginUserRole = securityUtil.getLoginUserRole(this.getRequest());

        HashMap<String, Object> map = new HashMap<>(2);
        map.put("loginUser",loginUser);
        map.put("loginUserRole",loginUserRole);
        renderJson(map);
    }

    /**
     * 登录、鉴权测试接口
     */
    @ActionKey("/sys/xtgl")
    public void xtgl() {
        renderText("系统管理...");
    }
    @ActionKey("/sys/yhgl")
    public void yhgl() {
        renderText("用户管理...");
    }
    @ActionKey("/portal/mhgl")
    public void mhgl() {
        renderText("网站门户管理...");
    }
    @ActionKey("/portal/index")
    public void portalIndex() {
        renderText("网站门户首页...");
    }
}

  效果

  未登录时,只有配置在无需登录即可访问的URL才能允许访问

  登录后,除了无需权限的URL,还可以访问角色允许访问的URL,注销后恢复登录前状态

  JFinal项目的整合代码在我的jfinal-demo项目中:不想用Spring全家桶?试试这个国产JFinal框架

  后记

  一套简单的登录、鉴权工具暂时先记录到这,后续再进行补充

版权声明

作者:huanzi-qch

出处:https://www.cnblogs.com/huanzi-qch

若标题中有“转载”字样,则本文版权归原作者所有。若无转载字样,本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.

声明:好星座所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流,版权归原作者huanzi一qch所有,原文出处。若您的权利被侵害,请联系 删除。

本文链接:https://www.haoxingzuo.com/w/9394.html