Shiro
简介
Apache强大的开源安全框架
认证、授权、企业会话管理、安全加密
Shiro和Spring Security简单比较
1. Shiro简单、灵活。Spring Security复杂、笨重。
2. Shiro可脱离Spring。Spring Security不可脱离Spring。
3. Shiro权限控制粒度较粗。Spring Security权限控制粒度更细。
Shiro整体架构
上边一层可理解为当前操作用户。
Security Manager是Shiro的核心,管理者其他组件。
Authenticator:认证器,登录。
Authorizer:授权,权限控制。
Session Manager:Session管理。Shiro自己实现了一套Session管理机制。
Session Dao:提供了Session的操作,增删改查。
Cache Manager:缓存管理器,来管理用户、角色、权限等的缓存的。
Pluggable Realms:可理解为Shiro和数据源之间的桥梁,Shiro获取认证信息,权限信息,角色数据通过Realms获取的。
Cryptography:密码加密。
Shiro认证
- 创建Security Manager
- 主体提交请求
- Security Manager通过Authenticator认证,Realm做最终认证。
//Realm
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before
public void addUser(){
simpleAccountRealm.addAccount("Mark", "123456");
}
@Test
public void testAuthentication(){
//1.构建Security Manager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("Mark","123456");
subject.login(token);
System.out.println("isAuthenticated : " + subject.isAuthenticated());
subject.logout();
System.out.println("isAuthenticated : " + subject.isAuthenticated());
}
打印结果为:
isAuthenticated : true
isAuthenticated : false
Shiro授权
- 创建Security Manager
- 主体授权
- Security Manager通过Authorizer授权,Realm获取角色权限数据。
//Realm
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before
public void addUser() {
simpleAccountRealm.addAccount("Mark", "123456", "admin");
}
@Test
public void testAuthorizer() {
//构建 SecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//主体提交请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("Mark","123456");
subject.login(token);
subject.checkRole("admin");
}
Shiro自定义Realm
内置Realm
IniRealm
IniRealm iniRealm = new IniRealm("classpath:user.ini");
//1.构建Security Manager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(iniRealm);
//2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("Mark", "123456");
subject.login(token);
System.out.println("isAuthenticated : " + subject.isAuthenticated());
subject.checkRole("admin");
subject.checkPermission("user:delete");
user.ini内容:
[users]
Mark=123456,admin
[roles]
admin=user:delete
打印结果为 isAuthenticated : true
JDBCRealm
DruidDataSource dataSource = new DruidDataSource();
{
dataSource.setUrl("jdbc:mysql://localhost:3309/how2java");
dataSource.setUsername("root");
dataSource.setPassword("root");
}
@Test
public void testJDBCRealm(){
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
//1.构建Security Manager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
//2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("Mark", "123456");
subject.login(token);
}
自定义Realm
public class CustomRealm extends AuthorizingRealm {
Map<String, String> userMap = new HashMap<String, String>(16);
{
userMap.put("Mark", "123456");
super.setName("custiomRealm");
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) principalCollection.getPrimaryPrincipal();
System.out.println(username);
//从数据库或缓存中获取角色数据
Set<String> roles = getRolesByUserName(username);
Set<String> permissions = getPermissionsByUserName(username);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
simpleAuthorizationInfo.setRoles(roles);
return simpleAuthorizationInfo;
}
/**
* 模拟从数据库中取权限数据
* @param username
* @return
*/
private Set<String> getPermissionsByUserName(String username) {
Set<String> sets = new HashSet<String>();
sets.add("user:delete");
sets.add("user.add");
return sets;
}
/**
* 模拟从数据库中取角色数据
* @param username
* @return
*/
private Set<String> getRolesByUserName(String username) {
Set<String> sets = new HashSet<String>();
sets.add("admin");
sets.add("user");
return sets;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从主体传过来的认证信息中,获得用户名
String username = (String) authenticationToken.getPrincipal();
//2.通过用户名到数据库中获取凭证
String password = getPasswordByUserName(username);
if (null == password)
return null;
SimpleAuthenticationInfo authenticationInfo
= new SimpleAuthenticationInfo("Mark", password, "customRealm");
return authenticationInfo;
}
/**
* 模拟数据库查询凭证
*
* @param username
* @return
*/
private String getPasswordByUserName(String username) {
return userMap.get(username);
}
@Test
public void testCustomRealm() {
CustomRealm customRealm = new CustomRealm();
//构建Security Manager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(customRealm);
//主体提交请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("Mark", "123456");
subject.login(token);
System.out.println(subject.isAuthenticated());
subject.checkRole("admin");
subject.checkPermission("user:delete");
}
Shiro 加密
Shiro散列配置
- HashedCredentialsMatcher
- 自定义Realm中使用散列
- 盐的使用
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//设置加密算法名称
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//设置加密次数
hashedCredentialsMatcher.setHashIterations(1);
//在自定义的Realm中设置加密实现
customRealm.setCredentialsMatcher(hashedCredentialsMatcher);
SimpleAuthenticationInfo authenticationInfo
= new SimpleAuthenticationInfo("Mark", password, "customRealm");
//认证的时候把盐加进去
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("Mark"));