鲁春利的工作笔记,好记性不如烂笔头
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;
}
// 代码略
}
转载于:https://blog.51cto.com/luchunli/1828080