澳门在线威尼斯官方 > 电脑数据库 > 权限框架,MySQL的Root用户密码

原标题:权限框架,MySQL的Root用户密码

浏览次数:140 时间:2019-10-22

缘由:最近北京市二环内大兴土木,各种挖沟埋线。忽而一纸通令周末断电,故多年不断电的服务器,便令人有了关机后是否还能正常启动的隐忧。其中一台较年迈的服务器中搭载有MySQL数据库。数据库内容本属于外包项目不需要多操心,但时至于此,为了数据安全备份一下,顺便查看下数据库的结构什么的,也在情理之中。但无奈的是,部署时间过于久远,无人清楚root的密码,由此引发的一系列思考和操作,便记录于此。

Apache Shiro 官网地址:

MySQL数据结构

MySQL在安装的时候会默认构建一个叫mysql的数据库,其中有名为user的表,其中主要定义了访问数据库的用户名密码以及CURD权限等。在访问MySQL的时候,会默认到user表里验证当前用户的信息是否匹配。

在首次安装MySQL的时候,默认会产生一个root用户,密码为空。权限固然是最大的,什么都能改,当然也是非常不安全的。所以一般安装之处都会给root设定一个密码。

Table user

Host User Password
localhost root *23AE809DDACAF96AF0FD78ED04B6A265E05AA257
% root *23AE809DDACAF96AF0FD78ED04B6A265E05AA257

从表获取的*23AE809DDACAF96AF0FD78ED04B6A265E05AA257便是root用户的密码,只不过这是加密之后的,也就是说,直接输入这么一长串字符串,是不正确的。于是乎出现了下面的疑问:

  • 在不知道密码的前提下,有方法知道root的密码么?
  • 在不知道密码的前提下,有方法访问数据库么?

Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.

MySQL加密方式

想要了解提出的疑问,首先还是先了解下MySQL的密文是怎么来的。
MySQL内置有PASSWORD()函数,目的就是将明文转换为密文,于是简单做了个实验。

1SELECT PASSWORD('123')

结果:*23AE809DDACAF96AF0FD78ED04B6A265E05AA257

说明明文123对应的密文就是这串带*的字符串。(所以我就很Lucky的知道了root的密码图片 1
但这依旧不能解决疑问,还是要从原理入手,于是查到了MySQL的加密算法SHA-1(百度百科-SHA-1算法)。

算法过程这里不涉及,但是需要了解的是SHA-1加密算法有如下特性

  • 不可以从消息摘要中复原信息
  • 两个不同的消息不会产生同样的消息摘要

简而言之,即使知道了密文,也没有办法通过算法的方式获取明文。
这简直就是直接断了后路…
可事实是Google在2017年时就已经宣布成功攻破SHA-1加密算法,只不过破解它需要的造价不是我等穷苦大众所能接受的。

现在网上也有很多的在线加密解密的工具,例如在线加密解密。但绝大部分在散列/哈希算法领域,也仅提供加密不提供解密。
所以结论依旧是,没办法!

退而求其次,终归是有办法让我们访问自己服务器上的数据库内容的吧?

shiro是一个强大而且简单易用的Java安全框架,主要功能有认证(就是登陆验证),授权(就是权限管理),加密(就是密码加密),session管理。适用于各种大型或者小型企业应用。和Spring Security比较而言,确实更加简单而且灵活易懂。

无密码MySQL访问方法

  1. cmd中的mysqlbin文件夹下执行下面语句,用于跳过用户验证访问数据库。
    当然,如果本机有正在运行的mysql的服务需要先停掉。
1mysqlbin>mysqld --skip-grant-tables
  1. 然后打开另外一个cmd,同样在mysqlbin文件夹下执行mysql访问数据库。
1mysqlbin>mysql
  1. 访问名为mysql的数据库。
1mysql>use mysql;
2database changed
  1. 查询user表中已有的用户(可以省略)。
1mysql>SELECT Host,User,Password FROM User;
  1. 如果要更改现有用户的密码,例如root@localhost
1mysql>UPDATE user SET Password=PASSWORD('123') WHERE User='root' AND Host='localhost';
  1. 现有的用户可能正在被某些应用使用,这种情况下可以只增加一个新的用户用于查看数据。
1mysql>INSERT INTO user (Host,User,Password) VALUES ('localhost','NewUser',PASSWORD('123'));

至此我们便有一个已知密码的用户,可以正常通过用户名密码访问数据库了。


1. shiro中的重要概念

扩展

SHA-1破解
常用加密方式AESDESSHA-256MD5BASE64

有兴趣的请自行扩展咯图片 2

要理解shiro,先要理解框架的几个概念:

1) Subject: 代表当前登陆或者访问的用户;

2)Principals:一般指用户名等,唯一表明Subject身份也就是当前用户身份的东西;

3)Credentials:凭证,一般指密码,对当前登陆用户进行验证;

4)Realms:域,一般是指存储用户信息(用户名,密码,权限,角色)的数据库,也就是保存用户权限等信息的数据源

5)SecurityManager:shiro安全管理的顶级对象。它集合或者说调用所有其它相关组件,负责所有安全和权限相关处理过程,就像一个中央集权政府;

2. shiro的子系统

上面我们说到shiro的主要功能有:认证,授权,加密,session管理等。而每一个主要功能对应于shiro的一个子系统:

图片 3

下面正对每一个子系统分别介绍。

3. Authentication认证子系统

认证子系统,就是处理用户登录,验证用户登录。我们前面讲到Subject代表当前用户,而Principal和credential分别就代表用户名和密码。登录认证时,其实就是调用的 Subject.login(AuthenticationToken token)方法,AuthenticationToken是一个接口:

public interface AuthenticationToken extends Serializable {
    /**
     * Returns the account identity submitted during the authentication process.*/
    Object getPrincipal();
    /**
     * Returns the credentials submitted by the user during the authentication process that verifies
     * the submitted {@link #getPrincipal() account identity}.*/
    Object getCredentials();
}

登录时就会分别调用它的实现类的 getPrincipal() 和 getCredentials() 来获得用户名(Principal:主体)和密码(Credentials:凭证)。一般实际中我们传给Subject.login()方法的是UsernamePasswordToken 类的对象,它实现了该接口:

图片 4

一般我们new一个UsernamePasswordToken的对象:UsernamePasswordToken token = new UsernamePasswordToken("xxxusername", "xxxpassword");, 然后 subject.login(token); 就前去登录。相关代码一般如下:

    @RequestMapping(value="/loginController", method=RequestMethod.POST)
    public String login(String userName, String password, String rememberMe, String type, HttpServletRequest req) {
        String error = null;
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        if(rememberMe != null && "true".equals(rememberMe))
            token.setRememberMe(true);    // 记住我        
        try {
            subject.login(token);
        } catch (UnknownAccountException | IncorrectCredentialsException e1) {
            error = "用户名或密码错误";
        }catch(ExcessiveAttemptsException e){
            userService.lockAccountByNo(no);     // 锁定账户
            error = "超过了尝试登录的次数,您的账户已经被锁定。";
        }catch (AuthenticationException e) {    // 其他错误
            if(e.getMessage() != null)
                error = "发生错误:" + e.getMessage();
            else
                error = "发生错误,无法登录。";
        }
        // .. ...

Authentication 子系统会将password加密,然后使用username和加密之后的password和从Realm(一般是数据库)中根据usename获得的密码进行比较,相同就登录成功,不相同同就登录失败,或者用户名不存在也登录失败。就怎么简单。当然从Realm中根据用户名查找用户的过程是需要我们自己编码实现的。该功能的实现,shiro提供了抽象类 AuthenticatingRealm 专门用于从Realm中获得认证信息。所以我们可以继承 抽象类 AuthenticatingRealm,然后实现其中的抽象方法:

/**
 * A top-level abstract implementation of the Realm interface that only implements authentication support
 * (log-in) operations and leaves authorization (access control) behavior to subclasses.
 */
public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {
    //TODO - complete JavaDoc
    private static final Logger log = LoggerFactory.getLogger(AuthenticatingRealm.class);

    // ... ...

    /**
     * Retrieves authentication data from an implementation-specific datasource (RDBMS, LDAP, etc) for the given
     * authentication token.
     * <p/>
     * For most datasources, this means just 'pulling' authentication data for an associated subject/user and nothing
     * more and letting Shiro do the rest.  But in some systems, this method could actually perform EIS specific
     * log-in logic in addition to just retrieving data - it is up to the Realm implementation.*/
    protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

我们只要实现 protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) 方法就可以了,其它的shiro会回调该方法,进行登录认证。而实现该方法就是直接从数据源中根据 AuthenticationToken 获得数据就行了。

除了这种方法之外,其实我们也可以使用 AuthenticatingRealm 的子类 AuthorizingRealm,它本来是用于权限认证的Realm,但是因为他继承了 AuthenticatingRealm,所以实际上我们只要继承 AuthorizingRealm,然后实现它的抽象方法就行了。同时搞定 登录认证 和 权限认证(访问控制):

public class UserRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String)principals.getPrimaryPrincipal();
        User user = userService.getUserByName(userName);
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRolesByUserId(user.getId()));
        authorizationInfo.setStringPermissions(userService.findPermissionsByUserId(user.getId()));
        return authorizationInfo;
    }    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String userName= (String)token.getPrincipal();
        User user = userService.getUserByName(userName);
        if(user == null) {
            throw new UnknownAccountException();//没找到账户
        }
        if(user.getLocked() == 0) {
            throw new LockedAccountException(); //帐号锁定
        }
        if(user.getLocked() == 2){
            throw new AuthenticationException("account was inactive");
        }

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUserName(), // 用户名
                user.getPassword(), // 密码
                ByteSource.Util.bytes(user.getCredentialsSalt()),    // salt
                getName()  // realm name
        );        
        return authenticationInfo;
    }
    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }
    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }
    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }
    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }
    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }
}

上面的 doGetAuthorizationInfo 方法,会在权限认证也就是访问控制时,被回调,而 doGetAuthenticationInfo 方法会在登录认证时被回调,返回的 AuthenticationInfo类型的对象,会和用户登录时输入的 用户名和密码(加密之后的)进行比较,相同则登录成功,反之则登录失败。

其实还有更加简单的方法,因为shiro提供了实现了 AuthorizingRealm 中的抽象方法的子类:

图片 5

比如在数据库环境中,我们就可以直接使用 JdbcRealm,一是可以配置它的相关SQL语句,二是继承它,覆盖它的方法。CasRealm用户单点登录环境。

/**
 * Realm that allows authentication and authorization via JDBC calls.  The default queries suggest a potential schema
 * for retrieving the user's password for authentication, and querying for a user's roles and permissions.  The
 * default queries can be overridden by setting the query properties of the realm.
 * <p/>
 * If the default implementation
 * of authentication and authorization cannot handle your schema, this class can be subclassed and the
 * appropriate methods overridden. (usually {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)},
 * {@link #getRoleNamesForUser(java.sql.Connection,String)}, and/or {@link #getPermissions(java.sql.Connection,String,java.util.Collection)}
 * <p/>
 * This realm supports caching by extending from {@link org.apache.shiro.realm.AuthorizingRealm}.*/
public class JdbcRealm extends AuthorizingRealm {
    //TODO - complete JavaDoc
    /*--------------------------------------------
    |             C O N S T A N T S             |
    ============================================*/
    /**
     * The default query used to retrieve account data for the user.
     */
    protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";    
    /**
     * The default query used to retrieve account data for the user when {@link #saltStyle} is COLUMN.
     */
    protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?/**
     * The default query used to retrieve the roles that apply to a user.
     */
    protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
    /**
     * The default query used to retrieve permissions that apply to a particular role.
     */
    protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";
    private static final Logger log = LoggerFactory.getLogger(JdbcRealm.class);    
    /**
     * Password hash salt configuration. <ul>
     *   <li>NO_SALT - password hashes are not salted.</li>
     *   <li>CRYPT - password hashes are stored in unix crypt format.</li>
     *   <li>COLUMN - salt is in a separate column in the database.</li> 
     *   <li>EXTERNAL - salt is not stored in the database. {@link #getSaltForUser(String)} will be called
     *       to get the salt</li></ul>
     */
    public enum SaltStyle {NO_SALT, CRYPT, COLUMN, EXTERNAL};
    /*--------------------------------------------
    |    I N S T A N C E   V A R I A B L E S    |
    ============================================*/
    protected DataSource dataSource;
    protected String authenticationQuery = DEFAULT_AUTHENTICATION_QUERY;
    protected String userRolesQuery = DEFAULT_USER_ROLES_QUERY;
    protected String permissionsQuery = DEFAULT_PERMISSIONS_QUERY;
    protected boolean permissionsLookupEnabled = false;    
    protected SaltStyle saltStyle = SaltStyle.NO_SALT;

我们可以对上面给出的sql语句进行配置,修改成对应于我们数据库中表的sql语句,也可以继承该类,然后覆盖doGetAuthenticationInfo, getRoleNamesForUser(), getPermissions()三个方法。

4. Authorization 授权子系统(访问控制)

上一节中我们已经介绍了如何获得用户所拥有的权限,在需要判断用户是否有某权限或者角色时,会自动回调方法 doGetAuthorizationInfo 来获得用户的角色和权限,我们只需要在 该方法中从Realm也就是数据库表中获得相关信息。我们先看一下shiro是如何表示角色和权限的,这一点比较重要:

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String no = (String)principals.getPrimaryPrincipal();
        User user = userService.getUserByNo(no);
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRolesByUserId(user.getId()));
        authorizationInfo.setStringPermissions(userService.findPermissionsByUserId(user.getId()));
        return authorizationInfo;
    }    

我们看到 doGetAuthorizationInfo 方法中使用了 SimpleAuthorizationInfo 类封装 Role 和 Permission.

/**
 * Simple POJO implementation of the {@link AuthorizationInfo} interface that stores roles and permissions as internal
 * attributes.
 * @see org.apache.shiro.realm.AuthorizingRealm
 * @since 0.9
 */
public class SimpleAuthorizationInfo implements AuthorizationInfo {
    /**
     * The internal roles collection.
     */
    protected Set<String> roles;
    /**
     * Collection of all string-based permissions associated with the account.
     */
    protected Set<String> stringPermissions;
    /**
     * Collection of all object-based permissions associaed with the account.
     */
    protected Set<Permission> objectPermissions;
    /**
     * Default no-argument constructor.
     */
    public SimpleAuthorizationInfo() {
    }

我们看到,roles 和 stringPermissions 都是 String 类型的 Set, 也就是说,它们都是使用字符串来表示你拥有某个角色或者拥有某个权限的

1) 两种访问控制方式:

SimpleAuthorizationInfo 封装了角色和权限,其实这也说明了实现“访问控制”两种方式:一是 “基于角色的访问控制”;而是“基于资源的访问控制”。所谓的访问控制,是指对于某个资源,当前用户是否有访问的权限。基于角色的访问控制是一种比较粗粒度的访问控制方式,只要你具有了某个或某几个角色,那么你就可以访问某资源。而基于资源的访问控制,是判断你针对该资源是否有某权限,有才能访问,粒度更细,你是否有某权限,可以根据你有哪些角色,然后改角色有哪些权限来判断的,当然也可以不引入角色的概念,直接判断你是否拥有某些权限。当然两种访问方式可以单独使用,也可以混合使用。比如对于比较简单的权限控制,你可以仅仅只使用基于角色的访问控制,仅仅引入角色表,不需要权限表都可以。混合使用是指,你可以同时要求用户具有某角色并且具有某些权限,才能访问某资源。所以shiro的权限控制时极其灵活的(当然也可以不引入角色表,仅仅引入权限表)。

2)权限的字符串表示方式

上面说到 角色 和 权限 都是使用字符串来表示的,其实 shiro 提供了一套比较强大有点复杂的权限字符串表示格式(分为:分割的三个部分):

资源:操作:对象实例ID” 表示:对那个资源的哪个实例可以进行哪些操作,支持通配符。

多个操作需要使用 “,” 逗号分割,而 “*” 放在三个位置上,分别表示:任意资源,任意操作,任意实例。

比如:"user:delete:1" 就表示 对user表的id等于1对应的数据或者对象,可以进行删除操作。其实资源表现实现可以是对象,其实最终是对应到数据库表中的记录。

在比如:"user:update,delete" 就表示 对user表(的任意实例)进行更新和删除操作。"user:update,delete" 其实就等价于 “user:update,delete:*”

所以 shiro 的访问控制可以控制到具体实例,或者说具体哪条数据库记录,也可以在表级别控制如果省略掉 对象实例ID部分,就是在表级别控制

3)权限相关表的设计

1> 如果对于简单的情况,可以只使用“基于角色的访问控制”粗粒度方式,不涉及到权限,仅仅只通过判断是否有某角色来判断访问控制,那么就只需要增加一个角色表(roles) 和 一个角色(roles)和用户(user)的多对多的一个中间表——用户角色表(user_role)。

2> 如果仅仅使用权限来控制访问,那么就可以仅仅只增加一个权限表(priv)和一个用户和权限的多对多的一个中间表——用户权限表(user_priv).

本文由澳门在线威尼斯官方发布于电脑数据库,转载请注明出处:权限框架,MySQL的Root用户密码

关键词:

上一篇:CentOS下安装MySQL

下一篇:安装报错找不到vc_red