• 8.13数据库认证


     

     

        /*--------------------------------------------
        |    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;
    /*
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */
    package org.apache.shiro.realm.jdbc;
    
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationException;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.config.ConfigurationException;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import org.apache.shiro.util.JdbcUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Collection;
    import java.util.LinkedHashSet;
    import java.util.Set;
    
    
    /**
     * 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}.
     *
     * @since 0.2
     */
    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;
    
        /*--------------------------------------------
        |         C O N S T R U C T O R S           |
        ============================================*/
    
        /*--------------------------------------------
        |  A C C E S S O R S / M O D I F I E R S    |
        ============================================*/
        
        /**
         * Sets the datasource that should be used to retrieve connections used by this realm.
         *
         * @param dataSource the SQL data source.
         */
        public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        /**
         * Overrides the default query used to retrieve a user's password during authentication.  When using the default
         * implementation, this query must take the user's username as a single parameter and return a single result
         * with the user's password as the first column.  If you require a solution that does not match this query
         * structure, you can override {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)} or
         * just {@link #getPasswordForUser(java.sql.Connection,String)}
         *
         * @param authenticationQuery the query to use for authentication.
         * @see #DEFAULT_AUTHENTICATION_QUERY
         */
        public void setAuthenticationQuery(String authenticationQuery) {
            this.authenticationQuery = authenticationQuery;
        }
    
        /**
         * Overrides the default query used to retrieve a user's roles during authorization.  When using the default
         * implementation, this query must take the user's username as a single parameter and return a row
         * per role with a single column containing the role name.  If you require a solution that does not match this query
         * structure, you can override {@link #doGetAuthorizationInfo(PrincipalCollection)} or just
         * {@link #getRoleNamesForUser(java.sql.Connection,String)}
         *
         * @param userRolesQuery the query to use for retrieving a user's roles.
         * @see #DEFAULT_USER_ROLES_QUERY
         */
        public void setUserRolesQuery(String userRolesQuery) {
            this.userRolesQuery = userRolesQuery;
        }
    
        /**
         * Overrides the default query used to retrieve a user's permissions during authorization.  When using the default
         * implementation, this query must take a role name as the single parameter and return a row
         * per permission with three columns containing the fully qualified name of the permission class, the permission
         * name, and the permission actions (in that order).  If you require a solution that does not match this query
         * structure, you can override {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} or just
         * {@link #getPermissions(java.sql.Connection,String,java.util.Collection)}</p>
         * <p/>
         * <b>Permissions are only retrieved if you set {@link #permissionsLookupEnabled} to true.  Otherwise,
         * this query is ignored.</b>
         *
         * @param permissionsQuery the query to use for retrieving permissions for a role.
         * @see #DEFAULT_PERMISSIONS_QUERY
         * @see #setPermissionsLookupEnabled(boolean)
         */
        public void setPermissionsQuery(String permissionsQuery) {
            this.permissionsQuery = permissionsQuery;
        }
    
        /**
         * Enables lookup of permissions during authorization.  The default is "false" - meaning that only roles
         * are associated with a user.  Set this to true in order to lookup roles <b>and</b> permissions.
         *
         * @param permissionsLookupEnabled true if permissions should be looked up during authorization, or false if only
         *                                 roles should be looked up.
         */
        public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) {
            this.permissionsLookupEnabled = permissionsLookupEnabled;
        }
        
        /**
         * Sets the salt style.  See {@link #saltStyle}.
         * 
         * @param saltStyle new SaltStyle to set.
         */
        public void setSaltStyle(SaltStyle saltStyle) {
            this.saltStyle = saltStyle;
            if (saltStyle == SaltStyle.COLUMN && authenticationQuery.equals(DEFAULT_AUTHENTICATION_QUERY)) {
                authenticationQuery = DEFAULT_SALTED_AUTHENTICATION_QUERY;
            }
        }
    
        /*--------------------------------------------
        |               M E T H O D S               |
        ============================================*/
    
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            UsernamePasswordToken upToken = (UsernamePasswordToken) token;
            String username = upToken.getUsername();
    
            // Null username is invalid
            if (username == null) {
                throw new AccountException("Null usernames are not allowed by this realm.");
            }
    
            Connection conn = null;
            SimpleAuthenticationInfo info = null;
            try {
                conn = dataSource.getConnection();
    
                String password = null;
                String salt = null;
                switch (saltStyle) {
                case NO_SALT:
                    password = getPasswordForUser(conn, username)[0];
                    break;
                case CRYPT:
                    // TODO: separate password and hash from getPasswordForUser[0]
                    throw new ConfigurationException("Not implemented yet");
                    //break;
                case COLUMN:
                    String[] queryResults = getPasswordForUser(conn, username);
                    password = queryResults[0];
                    salt = queryResults[1];
                    break;
                case EXTERNAL:
                    password = getPasswordForUser(conn, username)[0];
                    salt = getSaltForUser(username);
                }
    
                if (password == null) {
                    throw new UnknownAccountException("No account found for user [" + username + "]");
                }
    
                info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
                
                if (salt != null) {
                    info.setCredentialsSalt(ByteSource.Util.bytes(salt));
                }
    
            } catch (SQLException e) {
                final String message = "There was a SQL error while authenticating user [" + username + "]";
                if (log.isErrorEnabled()) {
                    log.error(message, e);
                }
    
                // Rethrow any SQL errors as an authentication exception
                throw new AuthenticationException(message, e);
            } finally {
                JdbcUtils.closeConnection(conn);
            }
    
            return info;
        }
    
        private String[] getPasswordForUser(Connection conn, String username) throws SQLException {
    
            String[] result;
            boolean returningSeparatedSalt = false;
            switch (saltStyle) {
            case NO_SALT:
            case CRYPT:
            case EXTERNAL:
                result = new String[1];
                break;
            default:
                result = new String[2];
                returningSeparatedSalt = true;
            }
            
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                ps = conn.prepareStatement(authenticationQuery);
                ps.setString(1, username);
    
                // Execute query
                rs = ps.executeQuery();
    
                // Loop over results - although we are only expecting one result, since usernames should be unique
                boolean foundResult = false;
                while (rs.next()) {
    
                    // Check to ensure only one row is processed
                    if (foundResult) {
                        throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique.");
                    }
    
                    result[0] = rs.getString(1);
                    if (returningSeparatedSalt) {
                        result[1] = rs.getString(2);
                    }
    
                    foundResult = true;
                }
            } finally {
                JdbcUtils.closeResultSet(rs);
                JdbcUtils.closeStatement(ps);
            }
    
            return result;
        }
    
        /**
         * This implementation of the interface expects the principals collection to return a String username keyed off of
         * this realm's {@link #getName() name}
         *
         * @see #getAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
            //null usernames are invalid
            if (principals == null) {
                throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
            }
    
            String username = (String) getAvailablePrincipal(principals);
    
            Connection conn = null;
            Set<String> roleNames = null;
            Set<String> permissions = null;
            try {
                conn = dataSource.getConnection();
    
                // Retrieve roles and permissions from database
                roleNames = getRoleNamesForUser(conn, username);
                if (permissionsLookupEnabled) {
                    permissions = getPermissions(conn, username, roleNames);
                }
    
            } catch (SQLException e) {
                final String message = "There was a SQL error while authorizing user [" + username + "]";
                if (log.isErrorEnabled()) {
                    log.error(message, e);
                }
    
                // Rethrow any SQL errors as an authorization exception
                throw new AuthorizationException(message, e);
            } finally {
                JdbcUtils.closeConnection(conn);
            }
    
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
            info.setStringPermissions(permissions);
            return info;
    
        }
    
        protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException {
            PreparedStatement ps = null;
            ResultSet rs = null;
            Set<String> roleNames = new LinkedHashSet<String>();
            try {
                ps = conn.prepareStatement(userRolesQuery);
                ps.setString(1, username);
    
                // Execute query
                rs = ps.executeQuery();
    
                // Loop over results and add each returned role to a set
                while (rs.next()) {
    
                    String roleName = rs.getString(1);
    
                    // Add the role to the list of names if it isn't null
                    if (roleName != null) {
                        roleNames.add(roleName);
                    } else {
                        if (log.isWarnEnabled()) {
                            log.warn("Null role name found while retrieving role names for user [" + username + "]");
                        }
                    }
                }
            } finally {
                JdbcUtils.closeResultSet(rs);
                JdbcUtils.closeStatement(ps);
            }
            return roleNames;
        }
    
        protected Set<String> getPermissions(Connection conn, String username, Collection<String> roleNames) throws SQLException {
            PreparedStatement ps = null;
            Set<String> permissions = new LinkedHashSet<String>();
            try {
                ps = conn.prepareStatement(permissionsQuery);
                for (String roleName : roleNames) {
    
                    ps.setString(1, roleName);
    
                    ResultSet rs = null;
    
                    try {
                        // Execute query
                        rs = ps.executeQuery();
    
                        // Loop over results and add each returned role to a set
                        while (rs.next()) {
    
                            String permissionString = rs.getString(1);
    
                            // Add the permission to the set of permissions
                            permissions.add(permissionString);
                        }
                    } finally {
                        JdbcUtils.closeResultSet(rs);
                    }
    
                }
            } finally {
                JdbcUtils.closeStatement(ps);
            }
    
            return permissions;
        }
        
        protected String getSaltForUser(String username) {
            return username;
        }
    
    }

    [main]
    # 定义本次MySQL连接要使用的数据源处理类
    dataSource=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
    # 定义数据库的连接主机名称
    dataSource.serverName=127.0.0.1
    # 定义要使用的数据库的名字
    dataSource.databaseName=shirodb
    # 定义数据库的连接账户
    dataSource.user=root
    # 定义数据库的连接的密码
    dataSource.password=
    
    # 定义本次要基于JDBC实现的Realm的认证的配置类;
    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    # 配置本次的JDBC连接所使用的数据源,$表示引用配置
    jdbcRealm.dataSource=$dataSource
    # 定义所需要的查询语句
    jdbcRealm.authenticationQuery=SELECT password FROM member WHERE mid=?
    # 配置安全管理器所使用的Realm
    securityManager.realms=$jdbcRealm
    -- 一、 删除数据库
    DROP DATABASE IF EXISTS shirodb;
    -- 二、创建数据库
    CREATE DATABASE shirodb CHARACTER SET UTF8 ;
    -- 三、使用数据库
    USE shirodb ;
    CREATE TABLE member (
        mid                  VARCHAR(50),
        password             VARCHAR(32),
        name                 VARCHAR(20),
        locked               INT,
        CONSTRAINT pk_mid PRIMARY KEY (mid)
    ) type = innodb;
    CREATE TABLE member (
        mid                  VARCHAR(50),
        password             VARCHAR(32),
        name                 VARCHAR(20),
        locked               INT,
        CONSTRAINT pk_mid PRIMARY KEY (mid)
    ) ENGINE = innodb;
    INSERT INTO member(mid,password,name,locked) VALUES ('admin','hello','管理员',0);
    INSERT INTO member(mid,password,name,locked) VALUES ('mermaid','hello','老李',0);
    INSERT INTO member(mid,password,name,locked) VALUES ('mldn','java','隔壁老王',0);
    package cn.mldn.test;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    //import org.apache.shiro.realm.jdbc.JdbcRealm;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    
    //import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
    
    
    
    //import org.apache.shiro.util.Factory;
    
    public class TestLoginDemo {
       public static void main(String[] args) {
           //MysqlDataSource 
           //JdbcRealm
           
           //Factory factory;
           //SecurityManager sm;
           // 取得Factory接口对象,主要的目的是通过配置文件加载文件之中的信息,这些信息暂时不能够成为认证信息
           Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
           // 取得里面所保存的所有的认证数据信息
           SecurityManager securityManager = factory.getInstance();
           //利用一个专门的认证操作的处理类,实现认证处理的具体的实现
           SecurityUtils.setSecurityManager(securityManager);
           // 获取进行用户名和密码认证的接口对象
           Subject subject = SecurityUtils.getSubject();
           // 定义了一个Token,里面保存要登录的用户名和密码信息
           UsernamePasswordToken token = new UsernamePasswordToken("admin","hello");
           // 实现用户登录处理
           subject.login(token);
           System.out.println(subject.getPrincipal());//取得用户名
       }
    }
    /*public interface Realm{
        *//**
         * 只是要求返回一个当前使用的Realm名字,这个名字可以任意返回,但是不要重名
         * @return
         *//*
        public String getName();
        *//**
         * 判断你当前使用的Token的类型是否为指定的类型
         * @param token
         * @return
         *//*
        public boolean supports(AuthenticationToken token) ;
        *//**
         * 得到用户的认证信息,根据传入的Token取得
         * @param token 包含了要进行验证的所有数据
         * @return
         * @throws AuthenticationException
         *//*
        public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
                throws AuthenticationException
    }
    */
    # Licensed to the Apache Software Foundation (ASF) under one or more
    # contributor license agreements.  See the NOTICE file distributed with
    # this work for additional information regarding copyright ownership.
    # The ASF licenses this file to You under the Apache License, Version 2.0
    # (the "License"); you may not use this file except in compliance with
    # the License.  You may obtain a copy of the License at
    #
    #      http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    # For the general syntax of property based configuration files see the
    # documenation of org.apache.log4j.PropertyConfigurator.
    
    # The root category uses the appender called A1. Since no priority is
    # specified, the root category assumes the default priority for root
    # which is DEBUG in log4j. The root category is the only category that
    # has a default priority. All other categories need not be assigned a
    # priority in which case they inherit their priority from the
    # hierarchy.
    
    log4j.rootCategory=, A1
    
    # A1 is set to be a LogMonitorAppender which outputs to a swing
    # logging console. 
      
    log4j.appender.A1=org.apache.log4j.lf5.LF5Appender
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>cn.mldn</groupId>
      <artifactId>shirodemo</artifactId>
      <packaging>war</packaging>
      <version>0.0.1-SNAPSHOT</version>
      <name>shirodemo Maven Webapp</name>
      <url>http://maven.apache.org</url>
      <dependencies>
        
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
        </dependency>
        <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
        </dependency>
        <!-- 日志开发包 -->
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/com.mchange/mchange-commons-java -->
        <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>mchange-commons-java</artifactId>
        <version>0.2.12</version>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
        <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.21</version>
        </dependency>
    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.21</version>
        <!--  
        <scope>test</scope>
        -->
    </dependency>
    
    
    
        
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.0.8</version>
    </dependency>
    
    
        
        
      </dependencies>
      <build>
        <finalName>shirodemo</finalName>
      </build>
    </project>
  • 相关阅读:
    Oracle 表空间查询
    FlaskAppBuilder 中文文档 markdown格式
    Springboot + Stomp + React 实现通过前后端通信
    Jmeter自动生成UUID
    Jmeter响应断言中,中文匹配失败问题解决
    IOS使用纯代码布局替换掉默认的storyboard
    MySQL在Windows上创建服务和删除服务
    IOS15上纯代码布局之导航控制器的导航条为透明的问题
    PHP编译FTP扩展
    ansible处理一些逻辑请求思路
  • 原文地址:https://www.cnblogs.com/ZHONGZHENHUA/p/8337732.html
Copyright © 2020-2023  润新知