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机制。