shiro的Realm
public class UserRealm extends AuthorizingRealm {
private UserService userService = new UserServiceImpl();
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.findRoles(username));
authorizationInfo.setStringPermissions(userService.findPermissions(username));
return authorizationInfo;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String)token.getPrincipal();
User user = userService.findByUsername(username);
if(user == null) {
throw new UnknownAccountException();//没找到帐号
}
if(Boolean.TRUE.equals(user.getLocked())) {
throw new LockedAccountException(); //帐号锁定
}
//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以在此判断或自定义实现
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user.getUsername(), //用户名
user.getPassword(), //密码
ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
getName() //realm name
);
return authenticationInfo;
}
}
1、UserRealm父类AuthorizingRealm将获取Subject相关信息分成两步:获取身份验证信息(doGetAuthenticationInfo)及授权信息(doGetAuthorizationInfo);
2、doGetAuthenticationInfo获取身份验证相关信息:首先根据传入的用户名获取User信息;然后如果user为空,那么抛出没找到帐号异常UnknownAccountException;
如果user找到但锁定了抛出锁定异常LockedAccountException;最后生成AuthenticationInfo信息,交给间接父类AuthenticatingRealm使用CredentialsMatcher进行判断密码是否匹配,
如果不匹配将抛出密码错误异常IncorrectCredentialsException;另外如果密码重试此处太多将抛出超出重试次数异常ExcessiveAttemptsException;
在组装SimpleAuthenticationInfo信息时,需要传入:身份信息(用户名)、凭据(密文密码)、盐(username+salt),CredentialsMatcher使用盐加密传入的明文密码和此处的密文密码进行匹配。
3、doGetAuthorizationInfo获取授权信息:PrincipalCollection是一个身份集合,因为我们现在就一个Realm,所以直接调用getPrimaryPrincipal得到之前传入的用户名即可;然后根据用户名调用UserService接口获取角色及权限信息。
我们来看下Realm类的继承关系:
一般我们使用的是AuthorizingRealm、CasRealm、JdbcRealm,AuthorizingRealm前面已经使用过了。CasRealm、JdbcRealm的使用例子如下:
@Bean(name = "myCasRealm")
public CasRealm myCasRealm(EhCacheManager cacheManager) {
CasRealm casRealm = new CasRealm();
casRealm.setCacheManager(cacheManager);
casRealm.setCasServerUrlPrefix(ShiroCasConfig.casServerUrlPrefix);
// 客户端回调地址 https://localhost:8081/cas 用来接收cas服务端票据,并且必须要和下面过滤器拦截的地址一致,拦截之后交由casFilter处理验证票据
casRealm.setCasService(ShiroCasConfig.shiroServerUrlPrefix + ShiroCasConfig.casFilterUrlPattern);
return casRealm;
} @Bean(name="myJdbcRealm")
public JdbcRealm myJdbcRealm(@Qualifier("shirocasDataSource") DataSource shirocasDataSource){
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(shirocasDataSource);
String authenticationQuery = "select password from account where name=?";
jdbcRealm.setAuthenticationQuery(authenticationQuery);
String userRolesQuery="SELECT NAME FROM role WHERE id =(SELECT roleId FROM account_role WHERE userId = (SELECT id FROM account WHERE NAME = ?))";
jdbcRealm.setUserRolesQuery(userRolesQuery);
String permissionsQuery = "SELECT NAME FROM permission WHERE id in (SELECT permissionId FROM permission_role WHERE (SELECT id FROM role WHERE NAME = ?))";
jdbcRealm.setPermissionsQuery(permissionsQuery);
jdbcRealm.setPermissionsLookupEnabled(true);
return jdbcRealm;
}
casRealm的部分源码:
/**
* This realm implementation acts as a CAS client to a CAS server for authentication and basic authorization.
* <p/>
* This realm functions by inspecting a submitted {@link org.apache.shiro.cas.CasToken CasToken} (which essentially
* wraps a CAS service ticket) and validates it against the CAS server using a configured CAS
* {@link org.jasig.cas.client.validation.TicketValidator TicketValidator}.
* <p/>
/**
* Authenticates a user and retrieves its information.
*
* @param token the authentication token
* @throws AuthenticationException if there is an error during authentication.
*/
@Override
@SuppressWarnings("unchecked")
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
CasToken casToken = (CasToken) token;
if (token == null) {
return null;
} String ticket = (String)casToken.getCredentials();
if (!StringUtils.hasText(ticket)) {
return null;
} TicketValidator ticketValidator = ensureTicketValidator(); try {
// contact CAS server to validate service ticket
Assertion casAssertion = ticketValidator.validate(ticket, getCasService());
// get principal, user id and attributes
AttributePrincipal casPrincipal = casAssertion.getPrincipal();
String userId = casPrincipal.getName();
log.debug("Validate ticket : {} in CAS server : {} to retrieve user : {}", new Object[]{
ticket, getCasServerUrlPrefix(), userId
}); Map<String, Object> attributes = casPrincipal.getAttributes();
// refresh authentication token (user id + remember me)
casToken.setUserId(userId);
String rememberMeAttributeName = getRememberMeAttributeName();
String rememberMeStringValue = (String)attributes.get(rememberMeAttributeName);
boolean isRemembered = rememberMeStringValue != null && Boolean.parseBoolean(rememberMeStringValue);
if (isRemembered) {
casToken.setRememberMe(true);
}
// create simple authentication info
List<Object> principals = CollectionUtils.asList(userId, attributes);
PrincipalCollection principalCollection = new SimplePrincipalCollection(principals, getName());
return new SimpleAuthenticationInfo(principalCollection, ticket);
} catch (TicketValidationException e) {
throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", e);
}
} /**
* Retrieves the AuthorizationInfo for the given principals (the CAS previously authenticated user : id + attributes).
*
* @param principals the primary identifying principals of the AuthorizationInfo that should be retrieved.
* @return the AuthorizationInfo associated with this principals.
*/
@Override
@SuppressWarnings("unchecked")
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// retrieve user information
SimplePrincipalCollection principalCollection = (SimplePrincipalCollection) principals;
List<Object> listPrincipals = principalCollection.asList();
Map<String, String> attributes = (Map<String, String>) listPrincipals.get();
// create simple authorization info
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// add default roles
addRoles(simpleAuthorizationInfo, split(defaultRoles));
// add default permissions
addPermissions(simpleAuthorizationInfo, split(defaultPermissions));
// get roles from attributes
List<String> attributeNames = split(roleAttributeNames);
for (String attributeName : attributeNames) {
String value = attributes.get(attributeName);
addRoles(simpleAuthorizationInfo, split(value));
}
// get permissions from attributes
attributeNames = split(permissionAttributeNames);
for (String attributeName : attributeNames) {
String value = attributes.get(attributeName);
addPermissions(simpleAuthorizationInfo, split(value));
}
return simpleAuthorizationInfo;
}
JdbcRealm的部分源码:
/**
* 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 = ?"; 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)[];
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[];
salt = queryResults[];
break;
case EXTERNAL:
password = getPasswordForUser(conn, username)[];
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;
} /**
* 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(, username); // Execute query
rs = ps.executeQuery(); // Loop over results and add each returned role to a set
while (rs.next()) { String roleName = rs.getString(); // 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(, 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(); // Add the permission to the set of permissions
permissions.add(permissionString);
}
} finally {
JdbcUtils.closeResultSet(rs);
} }
} finally {
JdbcUtils.closeStatement(ps);
} return permissions;
}
AuthenticationToken
AuthenticationToken用于收集用户提交的身份(如用户名)及凭据(如密码):
public interface AuthenticationToken extends Serializable {
Object getPrincipal(); //身份
Object getCredentials(); //凭据
}
扩展接口RememberMeAuthenticationToken:提供了“boolean isRememberMe()”现“记住我”的功能;
扩展接口是HostAuthenticationToken:提供了“String getHost()”方法用于获取用户“主机”的功能。
Shiro提供了一个直接拿来用的UsernamePasswordToken,用于实现用户名/密码Token组,另外其实现了RememberMeAuthenticationToken和HostAuthenticationToken,可以实现记住我及主机验证的支持。
AuthenticationInfo
public interface AuthenticationInfo extends Serializable {
PrincipalCollection getPrincipals();
Object getCredentials();
}
AuthenticationInfo有两个作用:
1、如果Realm是AuthenticatingRealm子类,则提供给AuthenticatingRealm内部使用的CredentialsMatcher进行凭据验证;(如果没有继承它需要在自己的Realm中自己实现验证);
2、提供给SecurityManager来创建Subject(提供身份信息);
CredentialsMatcher:
public interface CredentialsMatcher {
boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}
它的实现:
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenCredentials = getCredentials(token);
Object accountCredentials = getCredentials(info);
return equals(tokenCredentials, accountCredentials);
}
PrincipalCollection
因为我们可以在Shiro中同时配置多个Realm,所以呢身份信息可能就有多个;因此其提供了PrincipalCollection用于聚合这些身份信息:
public interface PrincipalCollection extends Iterable, Serializable {
Object getPrimaryPrincipal(); //得到主要的身份
<T> T oneByType(Class<T> type); //根据身份类型获取第一个
<T> Collection<T> byType(Class<T> type); //根据身份类型获取一组
List asList(); //转换为List
Set asSet(); //转换为Set
Collection fromRealm(String realmName); //根据Realm名字获取
Set<String> getRealmNames(); //获取所有身份验证通过的Realm名字
boolean isEmpty(); //判断是否为空
}
因为PrincipalCollection聚合了多个,此处最需要注意的是getPrimaryPrincipal,如果只有一个Principal那么直接返回即可,如果有多个Principal,则返回第一个(因为内部使用Map存储,所以可以认为是返回任意一个);
AuthorizationInfo
AuthorizationInfo用于聚合授权信息的:
public interface AuthorizationInfo extends Serializable {
Collection<String> getRoles(); //获取角色字符串信息
Collection<String> getStringPermissions(); //获取权限字符串信息
Collection<Permission> getObjectPermissions(); //获取Permission对象信息
}
当我们使用AuthorizingRealm时,如果身份验证成功,在进行授权时就通过doGetAuthorizationInfo方法获取角色/权限信息用于授权验证。
Shiro提供了一个实现SimpleAuthorizationInfo,大多数时候使用这个即可。
shiro的Realm的更多相关文章
- shiro自定义Realm
1.1 自定义Realm 上边的程序使用的是shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm. ...
- Shiro笔记(四)Shiro的realm认证
认证流程: 1.获取当前Subject.调用SecurityUtils.getSubject(); 2.测试当前用户是否已经被认证,即是否已经登录,调用Subject的isAurhenticated( ...
- Shiro中Realm
6.1 Realm [2.5 Realm]及[3.5 Authorizer]部分都已经详细介绍过Realm了,接下来再来看一下一般真实环境下的Realm如何实现. 1.定义实体及关系 即用户-角色 ...
- 自定义shiro的Realm实现和CredentialsMatcher实现以及Token实现
Realm是shiro比较核心的接口,简单说它的实现类就是校验用户输入的账号信息的地方.如果想自定义实现一般的配置文件如下: <!--自定义Realm 继承自AuthorizingRealm - ...
- 权限框架 - shiro 自定义realm
上篇文章中是使用的默认realm来实现的简单登录,这仅仅只是个demo,真正项目中使用肯定是需要连接数据库的 首先创建自定义realm文件,如下: 在shiro中注入自定义realm的完全限定类名: ...
- shiro多Realm第一次调用不生效问题
1. 由于最近自己写的一个项目上用到了多realm的使用,遇到了一个这样的问题: 1. 自己继承了BasicHttpAuthenticationFilter,实现了获取token,然后直接请求api的 ...
- shiro自定义realm支持MD5算法认证(六)
1.1 散列算法 通常需要对密码 进行散列,常用的有md5.sha, 对md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文. 建议对md5进行散列时加salt(盐),进行 ...
- shiro自定义realm认证(五)
上一节介绍了realm的作用: realm:需要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),如果查到用户返回认证信息,如果查询不到返回null.token就相当于是对用户输入 ...
- Shiro自定义Realm时用注解的方式注入父类的credentialsMatcher
用Shiro做登录权限控制时,密码加密是自定义的. 数据库的密码通过散列获取,如下,算法为:md5,盐为一个随机数字,散列迭代次数为3次,最终将salt与散列后的密码保存到数据库内,第二次登录时将登录 ...
- shiro双realm验证
假设现在有这样一种需求:存在两张表user和admin,分别记录普通用户和管理员的信息.并且现在要实现普通用户和管理员的分开登录,即需要两个Realm——UserRealm和AdminRealm,分别 ...
随机推荐
- 基于Jquery的实现回车键Enter切换焦点
系统默认情况下,使用Tab按键切换页面元素的焦点,有没有想过回车键Enter也可以实现这种功能,并且具有良好的用户体验. 接下来我们使用Jquery实现回车键Enter切换焦点,此代码在常用浏览器IE ...
- retrival and clustering : week 3 k-means 笔记
华盛顿大学 machine learning 笔记. K-means algorithm 算法步骤: 0. 初始化几个聚类中心 (cluster centers)μ1,μ2, … , μk 1. 将所 ...
- plsql类型
--plsql基本数据类型1.标量类型1.1数字型--BINARY_INTEGER 来存储有符号整数.它的范围是-2**31至2**31.跟PLS_INTEGER一样,BINARY_INTEGER所需 ...
- FOJ 1402(dp推规律)
推规律吧 /** 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...
- Eclipse UML插件
Green UML http://green.sourceforge.net/ AmaterasUML http://amateras.sourceforge.jp/cgi-bin/fswiki_en ...
- 160811、29 个你必须知道的 Linux 命令
虽然Linux发行版支持各种各样的饿GUI(graphical user interfaces),但在某些情况下,Linux的命令行接口(bash)仍然是简单快速的.Bash和 Linux Shell ...
- Xenu Link Sleuth
Xenu Link Sleuth 是一款检查网站死链接的软件,可以通过它打开一个本地的网页文件来检查它的链接,也可以输入任何网址来检查. 具体使用如下: 1,下载,并安装. 2,打开软件,出现 Tip ...
- win7(iis7)无法加载运行CSS文件的解决方法
在打开或关闭window功能中的Internet信息服务里的万维网服务=>常见HTTP功能=>静态内容 ,将其选上 即可了,如下图
- pro_update_role_pwd
DELIMITER | drop procedure if exists pro_update_role_pwd; CREATE PROCEDURE pro_update_role_pwd ( cro ...
- MyISAM Key Buffer 读/写/利用率(%) MylSAM平均每秒Key Buffer利用率(%) MylSAM平均每秒Key Buffer读命中率(%) MylSAM平均每秒Key Buffer写命中率(%)
MyISAM Key Buffer 读/写/利用率(%) MylSAM平均每秒Key Buffer利用率(%)MylSAM平均每秒Key Buffer读命中率(%)MylSAM平均每秒Key Buff ...