shiro中的概念:
Subject:代表当前登录或者访问的用户
Principals:一般指用户名等,唯一表明subject身份也就是当前用户身份的东西
Credentials:凭证,一般指密码,对当前登录用户进行验证
Realms:域,一般是指存储用户信息(用户名,密码,权限,角色)的数据库,也就是保存用户权限等信息的数据源
SecurityManager:shiro安全管理的顶级对象。它集合或者说调用所有其他相关组件,负责所有安全和权限相关处理过程,就像中央集权的政府。
shiro的子系统:
Shiro的主要功能:认证,授权,加密,session管理等,每一个主要功能对应于shiro的一个子系统
①Authentication认证子系统:处理用户登录,验证用户登录。
如:登录认证时调用subject.login(AuthenticationToken token)方法,AuthenticationToken是一个接口。会分别调用实现类的getPrincipal()和getCredentials()来获得用户名和密码。
Subject代表当前用户
通常做法:new一个UsernamePasswordToken的对象:
UsernamePasswordToken token = new UsernamePasswordToken(“username”,”password”);
然后subject.login(token);就去登录。
代码实例:
@RequestMapping(value="/loginController", method=RequestMethod.POST)
public String login(String userName, String password, String rememberMe, String type, HttpServletRequest req) {
String error = null;
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
if(rememberMe != null && "true".equals(rememberMe))
token.setRememberMe(true); // 记住我
try {
subject.login(token);
} catch (UnknownAccountException | IncorrectCredentialsException e1) {
error = "用户名或密码错误";
}catch(ExcessiveAttemptsException e){
userService.lockAccountByNo(no); // 锁定账户
error = "超过了尝试登录的次数,您的账户已经被锁定。";
}catch (AuthenticationException e) { // 其他错误
if(e.getMessage() != null)
error = "发生错误:" + e.getMessage();
else
error = "发生错误,无法登录。";
}
authentication子系统会将password进行加密,然后使用username和加密后的password和从数据库中根据username获的密码进行比较,相同就登录成功,不相同就登录失败,或者用户名不正确也会登录失败。
②authorization授权子系统(访问控制)
认证子系统会获得用户所拥有的权限,在需要判断用户是否有某权限或者是角色时,会自动回调方法doGetAuthorizationInfo来获得用户的角色和权限,我们只需要在数据库中获得相关信息。
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String no = (String)principals.getPrimaryPrincipal();
User user = userService.getUserByNo(no);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.findRolesByUserId(user.getId()));
authorizationInfo.setStringPermissions(userService.findPermissionsByUserId(user.getId()));
return authorizationInfo;
}
public class SimpleAuthorizationInfo implements AuthorizationInfo {
protected Set<String> roles;
protected Set<String> stringPermissions;
protected Set<Permission> objectPermissions;
public SimpleAuthorizationInfo() {
}
(1)使用字符串来表示你拥有某个角色或者拥有某个权限的
(2)两种访问控制方式:基于角色的访问控制,基于资源的访问控制
(3)访问控制:是指对于某个资源,当前用户是否有访问的权限。
(4)权限的字符串表示方式:
“资源:操作:对象实例ID”表示:对哪个资源的哪个实例可以进行哪些操作,支持通配符。
如:”user:delete:1”就表示对user表的id等于1对应的数据或者对象,可以进行删除操作。
“user:update,delete”表示对user表(的任意实例)进行更新和删除操作,等价于”user:update,delete:*”
所以shiro的访问控制可以控制到具体实例,或者说具体那条数据库记录,也可以在表级别控制。如果省略对象实例ID部分,就是在表级别控制。
(5)权限相关表的设计
A:简单情况,使用“基于角色的访问控制”,不涉及到权限,仅仅通过判断是否有某角色来判断访问控制,那么只需要增加一个角色表(roles)和一个角色_用户的多对多的中间表:user_roles
B:如果仅仅使用权限来控制访问,那么就可以仅仅增加一个权限表(priv)和一个权限_用户的中间表(user_priv)
C:如果既要用到角色,又要用到权限(权限根据角色推荐出来),那么就要增加:角色表,用户角色表,权限表,角色权限表。
③Cryptography加密子系统:
该子系统分为两部分:基于hash的单向加密算法,基于经典加密解密算法。
一般而言,对于登陆用户的密码的加密都是采用单向的hash加密算法,因为如果密码可以被解密的话,用户的密码会被破解。单向加密算法是无法被解密的。
1) 单向加密算法:一般使用sha系列算法,MD2/MD5安全性不足。
Sha256Hash算法:
public class Sha256Hash extends SimpleHash {
public static final String ALGORITHM_NAME = "SHA-256";
public Sha256Hash() {
super(ALGORITHM_NAME);
}
public Sha256Hash(Object source) {
super(ALGORITHM_NAME, source);
}
public Sha256Hash(Object source, Object salt) {
super(ALGORITHM_NAME, source, salt);
}
public Sha256Hash(Object source, Object salt, int hashIterations) {
super(ALGORITHM_NAME, source, salt, hashIterations);
}
public static Sha256Hash fromHexString(String hex) {
Sha256Hash hash = new Sha256Hash();
hash.setBytes(Hex.decode(hex));
return hash;
}
public static Sha256Hash fromBase64String(String base64) {
Sha256Hash hash = new Sha256Hash();
hash.setBytes(Base64.decode(base64));
return hash;
}
}
实现密码锁实例:
String sha256 = new Sha256Hash("admin", "11d23ccf28fc1e8cbab8fea97f101fc1d", 2).toString();
2) 双向经典加密解密算法
主要提供两种加密解密算法:AES、Blowfish
AES:
AesCipherService aesCipherService = new AesCipherService();
aesCipherService.setKeySize(128); // 设置key长度
// 生成key
Key key = aesCipherService.generateNewKey();
// 加密
String encrptText = aesCipherService.encrypt(text.getBytes(), key.getEncoded()).toHex();
// 解密
String text2 = new String(aesCipherService.decrypt(Hex.decode(encrptText), key.getEncoded()).getBytes());
System.out.println(text2.equals(text));
Key 表示 秘钥,就相当于 Hash 算法中的 salt,秘钥不同,最终的密文也就不同。不同的是解密时是需要使用加密时相同的秘钥才能解密成功。
Blowfish:
BlowfishCipherService blowfishService = new BlowfishCipherService();
blowfishService.setKeySize(128);
Key bKey = blowfishService.generateNewKey();
String encrpt = blowfishService.encrypt("admin".getBytes(), bKey.getEncoded()).toHex();
String dec = new String(blowfishService.decrypt(Hex.decode(encrpt), bKey.getEncoded()).getBytes());
System.out.println("admin".equals(dec));
使用 DefaultBlockCipherService 实现加密解密:
//使用Java的JCA(javax.crypto.Cipher)加密API,常见的如 AES, Blowfish
DefaultBlockCipherService cipherService = new DefaultBlockCipherService("AES");
cipherService.setKeySize(128);
//生成key
bKey = cipherService.generateNewKey();
text = "admin";
//加密
encrptText = cipherService.encrypt(text.getBytes(), key.getEncoded()).toHex();
//解密
text2 = new String(cipherService.decrypt(Hex.decode(encrptText), key.getEncoded()).getBytes());
System.out.println(text.equals(text2));
DefaultBlockCipherService(BlockCipher)是分组加密的意思,分组是指加密的过程是先进行分组,然后加密。AES 和 Blowfish都是分组加密算法。
④Session management会话管理子系统:
Shiro中session的最大的不同时,他可以使用在非web环境中,对于javaSE的环境也可以使用session的功能,因为他实现了一套不依赖web容器的session机制。