鲁春利的工作笔记,好记性不如烂笔头
Shiro默认提供的Realm
实际系统应用中一般继承AuthorizingRealm(授权)即可;其继承了AuthenticatingRealm(即身份验证),而且也间接继承了CachingRealm(带有缓存实现)。
Shiro Realm主要默认实现
org.apache.shiro.realm.text.IniRealm:
通过ini文件配置进行验证,
[users]部分指定用户名/密码及其角色;
[roles]部分指定角色即权限信息;
org.apache.shiro.realm.text.PropertiesRealm:
user.username=password,role1,role2 指定用户名/密码及其角色;
role.role1=permission1,permission2 指定角色及权限信息;
org.apache.shiro.realm.jdbc.JdbcRealm:
通过sql查询相应的信息, 如:
获取用户密码:“select“select password from users where username = ?”,
获取用户密码及盐:“select password, password_salt from users where username = ?”
获取用户角色:“select role_name from user_roles where username = ?”
获取角色对应的权限信息:“select permission from roles_permissions where role_name = ?”
也可以调用相应的api进行自定义sql。
JdbcRealm
1、数据库表
CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(20) DEFAULT NULL COMMENT '用户名', `password` varchar(50) DEFAULT NULL COMMENT '密码', `password_salt` varchar(10) DEFAULT NULL COMMENT '生成密码时用的随机种子', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
2、配置文件(shiro-authenticator-jdbc-realm.ini)
[main] # 配置JDBC数据库连接 dataSource=com.alibaba.druid.pool.DruidDataSource dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql://localhost:3306/spring_test dataSource.username=root dataSource.password=Mvtech123!@ # JdbcRealm jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm jdbcRealm.dataSource=$dataSource # 重写SQL查询 #jdbcRealm.authenticationQuery = SELECT password FROM ho_user WHERE name = ? #jdbcRealm.userRolesQuery = SELECT role FROM ho_user WHERE name = ? #jdbcRealm.permissionsQuery = SELECT permission FROM ho_user WHERE name = ? # 指定securityManager的realms实现 securityManager.realms=$jdbcRealm
3、测试类
@Test public void testAuthenticatorFromJDBCRealm () { // 1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro/shiro-authenticator-jdbc-realm.ini"); // 2、得到SecurityManager实例并绑定给SecurityUtils org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证) Subject subject = SecurityUtils.getSubject(); /* * 用户身份Token 可能不仅仅是用户名/密码,也可能还有其他的,如登录时允许用户名/邮箱/手机号同时登录。 */ UsernamePasswordToken token = new UsernamePasswordToken("lucl", "123"); try{ // 4、登录,即身份验证 subject.login(token); } catch (AuthenticationException e) { // 5、身份验证失败 logger.info("用户身份验证失败"); e.printStackTrace(); } if (subject.isAuthenticated()) { logger.info("用户登录成功。"); } else { logger.info("用户登录失败。"); } // 6、退出 subject.logout(); }
org.apache.shiro.realm.jdbc.JdbcRealm源码:
package org.apache.shiro.realm.jdbc; import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * Realm that allows authentication and authorization via JDBC calls. * 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; // 代码略 // 认证操作的代码 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; } // 代码略 }