auth 权限验证求助

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了auth 权限验证求助相关的知识,希望对你有一定的参考价值。

参考技术A Thinkphp权限认证Auth实例详解ThinkPHP权限认证Auth实例,本文以实例代码的形式深入剖析了ThinkPHP权限认证Auth的实现原理与方法,具体步骤如下:mysql数据库部分sql代码:?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172--------------------------------Tablestructureforthink_auth_group------------------------------DROPTABLEIFEXISTS`think_auth_group`;CREATETABLE`think_auth_group`(`id`mediumint(8)unsignedNOTNULLAUTO_INCREMENT,`title`char(100)NOTNULLDEFAULT'',`status`tinyint(1)NOTNULLDEFAULT'1',`rules`char(80)NOTNULLDEFAULT'',PRIMARYKEY(`id`))ENGINE=MyISAMAUTO_INCREMENT=2DEFAULTCHARSET=utf8COMMENT='用户组表';--------------------------------Recordsofthink_auth_group------------------------------INSERTINTO`think_auth_group`VALUES('1','管理组','1','1,2');--------------------------------Tablestructureforthink_auth_group_access------------------------------DROPTABLEIFEXISTS`think_auth_group_access`;CREATETABLE`think_auth_group_access`(`uid`mediumint(8)unsignedNOTNULLCOMMENT'用户id',`group_id`mediumint(8)unsignedNOTNULLCOMMENT'用户组id',UNIQUEKEY`uid_group_id`(`uid`,`group_id`),KEY`uid`(`uid`),KEY`group_id`(`group_id`))ENGINE=MyISAMDEFAULTCHARSET=utf8COMMENT='用户组明细表';--------------------------------Recordsofthink_auth_group_access------------------------------INSERTINTO`think_auth_group_access`VALUES('1','1');INSERTINTO`think_auth_group_access`VALUES('1','2');--------------------------------Tablestructureforthink_auth_rule------------------------------DROPTABLEIFEXISTS`think_auth_rule`;CREATETABLE`think_auth_rule`(`id`mediumint(8)unsignedNOTNULLAUTO_INCREMENT,`name`char(80)NOTNULLDEFAULT''COMMENT'规则唯一标识',`title`char(20)NOTNULLDEFAULT''COMMENT'规则中文名称',`status`tinyint(1)NOTNULLDEFAULT'1'COMMENT'状态:为1正常,为0禁用',`type`char(80)NOTNULL,`condition`char(100)NOTNULLDEFAULT''COMMENT'规则表达式,为空表示存在就验证,不为空表示按照条件验证',PRIMARYKEY(`id`),UNIQUEKEY`name`(`name`))ENGINE=MyISAMAUTO_INCREMENT=5DEFAULTCHARSET=utf8COMMENT='规则表';--------------------------------Recordsofthink_auth_rule------------------------------INSERTINTO`think_auth_rule`VALUES('1','Home/index','列表','1','Home','');INSERTINTO`think_auth_rule`VALUES('2','Home/add','添加','1','Home','');INSERTINTO`think_auth_rule`VALUES('3','Home/edit','编辑','1','Home','');INSERTINTO`think_auth_rule`VALUES('4','Home/delete','删除','1','Home','');DROPTABLEIFEXISTS`think_user`;CREATETABLE`think_user`(`id`int(11)NOTNULL,`username`varchar(30)DEFAULTNULL,`password`varchar(32)DEFAULTNULL,`age`tinyint(2)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8;--------------------------------Recordsofthink_user------------------------------INSERTINTO`think_user`VALUES('1','admin','21232f297a57a5a743894a0e4a801fc3','25');配置文件Application\Common\Conf\config.php部分:?12345678910111213141516171819202122'配置值''DB_DSN'=>'',//数据库连接DSN用于PDO方式'DB_TYPE'=>'mysql',//数据库类型'DB_HOST'=>'localhost',//服务器地址'DB_NAME'=>'thinkphp',//数据库名'DB_USER'=>'root',//用户名'DB_PWD'=>'root',//密码'DB_PORT'=>3306,//端口'DB_PREFIX'=>'think_',//数据库表前缀'AUTH_CONFIG'=>array('AUTH_ON'=>true,//认证开关'AUTH_TYPE'=>1,//认证方式,1为时时认证;2为登录认证。'AUTH_GROUP'=>'think_auth_group',//用户组数据表名'AUTH_GROUP_ACCESS'=>'think_auth_group_access',//用户组明细表'AUTH_RULE'=>'think_auth_rule',//权限规则表'AUTH_USER'=>'think_user'//用户信息表));项目Home控制器部分Application\Home\Controller\IndexController.class.php代码:?123456789101112131415161718192021222324check($name,$uid,$type,$mode,$relation))die('认证:成功');elsedie('认证:失败');以上这些代码就是最基本的验证代码示例。下面是源码阅读:1、权限检验类初始化配置信息:?1$Auth=new\Think\Auth();创建一个对象时程序会合并配置信息程序会合并Application\Common\Conf\config.php中的AUTH_CONFIG数组?1234567891011publicfunction__construct()$prefix=C('DB_PREFIX');$this->_config['AUTH_GROUP']=$prefix.$this->_config['AUTH_GROUP'];$this->_config['AUTH_RULE']=$prefix.$this->_config['AUTH_RULE'];$this->_config['AUTH_USER']=$prefix.$this->_config['AUTH_USER'];$this->_config['AUTH_GROUP_ACCESS']=$prefix.$this->_config['AUTH_GROUP_ACCESS'];if(C('AUTH_CONFIG'))//可设置配置项AUTH_CONFIG,此配置项为数组。$this->_config=array_merge($this->_config,C('AUTH_CONFIG'));2、检查权限:?1check($name,$uid,$type=1,$mode='url',$relation='or')大体分析一下这个方法首先判断是否关闭权限校验如果配置信息AUTH_ON=>false则不会进行权限验证否则继续验证权限?123if(!$this->_config['AUTH_ON'])returntrue;获取权限列表之后会详细介绍:?1$authList=$this->getAuthList($uid,$type);此次需要验证的规则列表转换成数组:?12345678if(is_string($name))$name=strtolower($name);if(strpos($name,',')!==false)$name=explode(',',$name);else$name=array($name);所以$name参数是不区分大小写的,最终都会转换成小写开启url模式时全部转换为小写:?123if($mode=='url')$REQUEST=unserialize(strtolower(serialize($_REQUEST)));权限校验核心代码段之一,即循环所有该用户权限判断当前需要验证的权限是否在用户授权列表中:?12345678910111213foreach($authListas$auth)$query=preg_replace('/^.+\?/U','',$auth);//获取url参数if($mode=='url'&&$query!=$auth)parse_str($query,$param);//获取数组形式url参数$intersect=array_intersect_assoc($REQUEST,$param);$auth=preg_replace('/\?.*$/U','',$auth);//获取访问的url文件if(in_array($auth,$name)&&$intersect==$param)//如果节点相符且url参数满足$list[]=$auth;elseif(in_array($auth,$name))$list[]=$auth;in_array($auth,$name)如果权限列表中其中一条权限等于当前需要校验的权限则加入到$list中注:?12345678910111213$list=array();//保存验证通过的规则名if($relation=='or'and!empty($list))returntrue;$diff=array_diff($name,$list);if($relation=='and'andempty($diff))returntrue;$relation=='or'and!empty($list);//当or时只要有一条是通过的则权限为真$relation=='and'andempty($diff);//当and时$name与$list完全相等时权限为真3、获取权限列表:?1$authList=$this->getAuthList($uid,$type);//获取用户需要验证的所有有效规则列表这个主要流程:获取用户组?12$groups=$this->getGroups($uid);//SELECT`rules`FROMthink_auth_group_accessaINNERJOINthink_auth_groupgona.group_id=g.idWHERE(a.uid='1'andg.status='1')简化操作就是:?1SELECT`rules`FROMthink_auth_groupWHERESTATUS='1'ANDid='1'//按正常流程去think_auth_group_access表中内联有点多余.!取得用户组rules规则字段这个字段中保存的是think_auth_rule规则表的id用,分割$ids就是$groups变量最终转换成的id数组:?12345$map=array('id'=>array('in',$ids),'type'=>$type,'status'=>1,);取得think_auth_rule表中的规则信息,之后循环:?123456789101112131415foreach($rulesas$rule)if(!empty($rule['condition']))//根据condition进行验证$user=$this->getUserInfo($uid);//获取用户信息,一维数组$command=preg_replace('/\(\w*?)\/','$user[\'\\1\']',$rule['condition']);//dump($command);//debug@(eval('$condition=('.$command.');'));if($condition)$authList[]=strtolower($rule['name']);else//只要存在就记录$authList[]=strtolower($rule['name']);if(!empty($rule['condition']))//根据condition进行验证这里就可以明白getUserInfo会去获取配置文件AUTH_USER对应表名去查找用户信息重点是:?12$command=preg_replace('/\(\w*?)\/','$user[\'\\1\']',$rule['condition']);@(eval('$condition=('.$command.');'));'/\(\w*?)\/可以看成要匹配的文字为字符串那么字符串会替换成$user['字符串']$command=$user['字符串']如果?12345$rule['condition']='age';$command=$user['age']$rule['condition']='age>5';$command=$user['age']>10@(eval('$condition=('.$command.');'));即:?1$condition=($user['age']>10);这时再看下面代码如果为真则加为授权列表?123if($condition)$authList[]=strtolower($rule['name']); 参考技术B 现象
一线的工程师反映了一个奇怪的现象,刚刚从 MySQL 官网上下载了一个 MySQL 5.7.31。安装完成后,发现使用任何密码都能登陆 MySQL,修改密码也不管用,重新启动 MySQL 也不能解决。

分析
怀疑使用了 --skip-grant-tables 使用 mysqld --print-defaults 检查,没有发现。
检查登陆用户,都是 root@localhost,说明和 proxy user 没有关系。
使用 mysql --print-defaults 检查客户端是否设置默认的用户和密码,没有发现。
检查数据库中的用户和密码的相关字段:
发现一切都正常,再检查 plugin 字段,发现只有 root 用户是 auth_socket ,其它的用户都是 mysql_native_password,问题可能就出在这儿。

对 auth_socket 验证插件不了解,感觉是这个插件不安全,使用下面的命令修改后,问题解决:
update user set plugin="mysql_native_password" where user='root';
auth_socket 验证插件的使用场景
问题解决后,又仔细研究了一下 auth_socket 这个插件,发现这种验证方式有以下特点:
首先,这种验证方式不要求输入密码,即使输入了密码也不验证。这个特点让很多人觉得很不安全,实际仔细研究一下这种方式,发现还是相当安全的,因为它有另外两个限制;
只能用 UNIX 的 socket 方式登陆,这就保证了只能本地登陆,用户在使用这种登陆方式时已经通过了操作系统的安全验证;
操作系统的用户和 MySQL 数据库的用户名必须一致,例如你要登陆MySQL 的 root 用户,必须用操作系统的 root用户登陆。
auth_socket 这个插件因为有这些特点,它很适合我们在系统投产前进行安装调试的时候使用,而且也有相当的安全性,因为系统投产前通常经常同时使用操作系统的 root 用户和 MySQL 的 root 用户。当我们在系统投产后,操作系统的 root 用户和 MySQL 的 root 用户就不能随便使用了,这时可以换成其它的验证方式,可以使用下面的命令进行切换:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'test';

登陆权限--token 的生成和验证

 在前后端分离时,实现登陆权限控制。 在这使用token来处理,提前说明后端采用的是SSM框架 

后端需要完成的操作如下:

1、生成token的工具类TokenUtil(内含用户名+密码生成token值和token验证是否正确的两个方法):

package com.lw.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class TokenUtil {
private static final long EXPIRE_TIME = 15 * 60 * 1000; // 设置过期时间为15分钟
private static final String TOKEN_SECRET = "JNVCOR&$@)_(#VSZ+_?CV}{XNVM^&"; // 设置签名私钥
/**
* 生成token,可以将你认为需要的数据当成参数存入
*/
public static String sign(String userName,String password) {
try {
// 设置过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
// 私钥和加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
// 设置头部信息
Map<String, Object> header = new HashMap<String, Object>(2);
header.put("Type", "Jwt");
header.put("alg", "HS256");
// 返回token字符串
return JWT.create()
.withHeader(header)
.withClaim("userName", userName)
.withClaim("password",password)
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 检验token是否正确语句
*/
public static boolean verify(String token){
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception e){
return false;
}
}

/**
* 获取token中的参数值
* @param token
* @return
*/
public static String getUserName(String token){
try{
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("userName").asString();
}catch (Exception e){
return null;
}

}
/**
* 获取token中的参数值
* @param token
* @return
*/
public static String getPassword(String token){
try{
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("password").asString();
}catch (Exception e){
return null;
}

}

}

2、导入工具类TokenInterceptor(获取前端token值进行校验是否相等)

package com.lw.controller.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.lw.util.Result;
import com.lw.util.TokenUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle) throws Exception {

response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Content-Type", "application/json;charset=utf-8");
String token = request.getHeader("token"); //获取在前端存储的token值。

response.setCharacterEncoding("utf-8");
//跨域ajax请求,都会先发一次method为OPTIONS的预请求
//1、获取服务器支持的HTTP请求方法。
//2、用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。
if (request.getMethod().equals("OPTIONS")) {
System.out.println("OPTIONS");
response.setStatus(HttpServletResponse.SC_OK);
return true;

}
else if (null != token) {
           //token不为空,验证是否token是否正确
if ( TokenUtil.verify(token)) {
return true;
}
}
Result result = Result.err("用户过期,请重新登录");
String json = JSONObject.toJSONString(result);
response.getWriter().write(json);
return false;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

}
}


3、在Spring-mvc.xml文件中配置token拦截器:(起拦截作用,可设置实现未登录时不能访问该页面权限)
 <!--配置token校验拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/topic/comment"/>
<mvc:mapping path="/topic/send"/>
<mvc:mapping path="/topic/like"/>
<mvc:mapping path="/topic/unlike"/>
<mvc:mapping path="/topic/delete"/>
<mvc:mapping path="/home/user/profile"/>
<mvc:mapping path="/home/user/editSubmit"/>
<bean class="com.lw.controller.interceptor.TokenInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

以上是关于auth 权限验证求助的主要内容,如果未能解决你的问题,请参考以下文章

休息 |智威汤逊 | Auth:如何验证安全资源的权限

thinkphp auth 后端权限验证。

登陆权限--token 的生成和验证

登陆权限--token 的生成和验证

django权限验证装饰器

Django rest framework 身份和权限验证