Shiro入门(一)

0. Shiro是什么?

简单的来说
Apache Shiro 是一个强大灵活的开源安全框架,可以完全处理身份验证、授权、加密和会话管理。

shiro的四大基石

  1. Authentication(认证):用户身份识别,通常被称为用户“登录”

  2. Authorization(授权): 访问控制。比如某个用户是否具有某个操作的使用权限。

  3. Session Management(会话管理): 特定于用户的会话管理,甚至在非web 或 EJB 应用程序。

  4. Cryptography(加密): 在对数据源使用加密算法加密的同时,保证易于使用。

Shrio的三个理念(重要)

Subject:
当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
SecurityManager:
管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
Realms:
用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。

1. Shiro的简单使用

1.1 导入依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.0</version>
</dependency>

1.2 创建一个测试类

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class AuthenticationTest {
    SimpleAccountRealm simpleAccountRealm = new  SimpleAccountRealm();

    @Before
    public void addUser(){
    //用户名,密码,权限。。。
        simpleAccountRealm.addAccount("liliya","123456","admin","user");
    }

    @Test
    public void testAuthentication(){
        //1. 构建Security环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);

        //2. 主题提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();  //获取当前的主体

        //3. 模拟用户登陆
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("liliya","123456");
        subject.login(usernamePasswordToken);

        System.out.println("用户登录:"+(subject.isAuthenticated()?"成功!":"失败"));
        //检查用户是否有权限
        subject.checkRole("admin");
        //退出登录
        subject.logout();
        System.out.println("用户登录:"+(subject.isAuthenticated()?"成功!":"失败"));

    }
}

在这里插入图片描述

总的流程就是这样

  1. 先创建一个Realm,用于权限的验证
  2. 构建SecurityManager管理Realm
  3. 通过Shiro的SecurityUtils设置SecurityManager
  4. 通过SecurityUtils来获得当前对象Subject
  5. subject.login登录

如果登录失败会怎么样?

这里我故意打错,可以得到。
密码错误就是: IncorrectCredentialsException
在这里插入图片描述

用户不存在就是: UnknownAccountException
在这里插入图片描述

权限不存在:UnauthorizedException

在这里插入图片描述

2. 自定义Realm

搞一个配置类,继承 AuthorizingRealm ,要重写两个方法,一个是用来授权的,一个是用来认证的。。。

public class MyRealm extends AuthorizingRealm {
    /**
     * 授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    /**
     * 认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        return null;
    }
}

2.1 例子

public class MyRealm extends AuthorizingRealm {
        HashMap<String, String> map = new HashMap<>();
    {
        map.put("liliya","123456");
        map.put("admin","admin");
        map.put("root","root");
        super.setName("myRealm"); //配置当前realm的名字
    }
    /**
     * 授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //获取用户名
        String userName = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        //假装从数据库拿到权限和角色
        Set<String> roles = queryRolesByUserName(userName);
        Set<String> permissions = queryPermissionByUserName(userName);
        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }

    private Set<String> queryPermissionByUserName(String userName) {
        HashSet<String> permissions = new HashSet<>();
        permissions.add("add");
        permissions.add("query");
        permissions.add("update");
        permissions.add("delete");
        return permissions;
    }

    //假装从数据库拿到了权限
    private Set<String> queryRolesByUserName(String userName) {
        HashSet<String> roles = new HashSet<>();
        roles.add("admin");
        roles.add("user");
        return roles;
    }

    /**
     * 认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 1.从主体传过来的认证信息中,获得用户名
        String userName = (String) token.getPrincipal();

        // 2.通过用户名到数据库中获取凭证
        String password = getPasswordByUserName(userName);
        if (password == null) {
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, password, "myRealm");
        return authenticationInfo;
    }

    //假装从数据库查到了数据
    private String getPasswordByUserName(String userName) {
        return map.get(userName);
    }
}

2.2 测试

public class AuthenticationTest3 {
    @Test
    public void auth(){
        MyRealm myRealm = new MyRealm();

        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(myRealm);
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        //模拟登录
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("liliya","123456");
        subject.login(usernamePasswordToken);

        System.out.println((subject.isAuthenticated())?"登录成功":"登录失败");

        //检查用户是否是admin角色
        subject.checkRole("admin");

        //检查用户是否有add权限
        subject.checkPermissions("add");
    }
}

3. Shiro自带加密

String password = "123456";
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
int times = 2;  // 加密次数:2
String alogrithmName = "md5";   // 加密算法

String encodePassword = new SimpleHash(alogrithmName, password, salt, times).toString();

System.out.printf("原始密码是 %s , 盐是: %s, 运算次数是: %d, 运算出来的密文是:%s ",password,salt,times,encodePassword);

参考链接:
https://zhuanlan.zhihu.com/p/54176956

posted @ 2021-04-04 11:07  莉莉娅酱  阅读(30)  评论(0)    收藏  举报