Shiro入门

自学Shiro总结

前言

shiro入门

简介Shiro

Apache Shiro框架是Apache旗下的一个开源安全框架。它提供了验证(Authentication)、授权(Authorization)、会话管理(Session Management)以及加密(Cryptography)这四个主要功能。(只是简单的学习了四个最主要的部分)
shiro支持的功能
Authentication(认证):用户身份识别,通常称呼这一个过程为"登录"
Authorization(授权):访问控制。例如某个用户是否拥有要进行操作的权限
Session Management(会话管理):特定于用户的会话管理。(很重要的一点就是可以在非web的环境下使用)
Cryptography(加密):对数据源使用加密算法加密的同时方便简易的使用

在程序中使用

Shiro的官方文档里有详细说明Shiro的使用需求“Ensure you have JDK 1.6+
所以要想使用Shiro首先必须要拥有的环境是JDK1.6及更高版本
然后在pom.xml文件中添加如下依赖

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

执行概述(简化版)
Shiro的执行大概
Subject:“当前用户”(too many applications have existing APIs that already have their own User classes/frameworks, and we didn’t want to conflict with those。其实官方文档已经说明了,并不希望把这个类当成一个用户来看,害怕影响到开发者自己的类或者框架。但是这个类在程序中的作用及用处,你可以把它当成一个"用户")它可以是一个人,也可以是第三方服务,或者是一切能与程序交互的东西
SecurityManager:Shiro的核心,管理所有的Subject
Realm:用于进行权限验证,由开发者自己实现(本质上可以说是一个特定的安全DAO,它负责与数据源连接的细节,得到Shiro需要的数据。在配置Shiro的时候必须要指定一个Realm来实现认证Authentication和/或authorization)

Shiro的认证过程(注意区分一下认证和验证,理解上会有些不同)

1.创建SecurityManager
2.主体提交认证
3.SecurityManager认证
4.Authenticator认证
5.Realm验证

配置好框架环境之后就可以开始尝试使用了
首先创建一个单元测试AccountTest.java
在类中添加如下代码

		
	public class Authentication {
    // 创建一个全局账户领域
    SimpleAccountRealm accountRealm = new SimpleAccountRealm();

    @Before
    // 在测试开始之前添加一个用户
    public void addUser(){
        accountRealm.addAccount("userName","password");
    }

    @Test
    public void testAuthentication(){
        // 构建SecurityManager环境
        DefaultSecurityManager defaultSecurity = new DefaultSecurityManager();
        defaultSecurity.setRealm(accountRealm);

        // 主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurity); // 设置SecurityManager环境
        Subject subject = SecurityUtils.getSubject(); // 获取当前主体

        UsernamePasswordToken token = new UsernamePasswordToken("userName","password");
        subject.login(token);

        // 使用subject.authenticated()方法返回boolean值,来判断用户是否登录成功
        System.out.println(subject.isAuthenticated()?"登录成功":"登录失败");
        subject.logout();
        System.out.println(subject.isAuthenticated()?"登录成功":"退出登录");
    }
}

控制台运行结果为:
登录成功
退出登录

这是官方给你认证执行流程
官方给的执行流程
1.首先调用Subject对象的login()方法来进行登录,会自动提交到SecurityManager来处理这个请求。(调用之前需要提前设置好SecurityManager,SecurityUtils.serSecurityManager(自己构建的security))
2.SecurityManeger负责将身份验证委托给Authenticator
3.Authenticator才是真正的身份验证者,Shiro框架的核心API的入口。这里可以插入自己实现的验证
4.Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;(这里是照搬的,其实自己也没有很搞懂这几个模块。但是能总结一点就是进行了一个或多个Realm验证)
5.Authenticator会把相应的token传入Realm,从Realm中获取身份验证信息。如果没有返回/或者发生了异常,则代表身份验证失败了。这里可以配置很多Realm。

Shiro的授权过程

1.创建SecurityManager
2.主体授权
3.SecurityManager授权
4.Authorizer授权
5.Realm获取角色授权数据
##和认证的过程比较相似

和认证一样首先创建一个Authorization.java的单元测试类
然后在类中添加如下代码

public class Authorization {

SimpleAccountRealm accountRealm = new SimpleAccountRealm();

@Before // 在方法执行开始之前添加一个用户,并让他同时具有admin和user两种角色
public void addUser(){
    accountRealm.addAccount("userName","password","admin","user");
}

@Test
public void testAuthorization(){
    // 1.构建SecurityManager环境
    DefaultSecurityManager defaultSecurity = new DefaultSecurityManager();
    defaultSecurity.setRealm(accountRealm);

    // 2.主体提交认证请求
    SecurityUtils.setSecurityManager(defaultSecurity); // 设置SecurityManager
    Subject subject = SecurityUtils.getSubject(); // 获得当前主体

    UsernamePasswordToken token = new UsernamePasswordToken("userName", "password");
    subject.login(token); // 登录

    System.out.println(subject.isAuthenticated()?"登录成功":"登录失败"); // 判断登录是否成功
    // 判断这个主体是否同时拥有admin和user两种角色,如果没有将会报错
   subject.checkRoles("admin","user");
 }
}

控制台结果为:
登录成功

自定义Realm

从上面的文章上来看,无论是验证还是授权,都离不开XXXRealm这个类。Shiro官方也提供了两种Realm,一种是查询ini文件的IniRealm,一种是查询数据库的JdbcRealm。而我们再自己的程序中可能会遇到各种比较另类的登录需求,这个时候就可以自己来定义Realm,来实现自己需要实现验证的功能。
首先自己创建一个类MyRealm,继承Shiro框架的AuthorizingRealm类,实现默认的两个方法。

	package realm;

	import org.apache.shiro.authc.AuthenticationException;
	import org.apache.shiro.authc.AuthenticationInfo;
	import org.apache.shiro.authc.AuthenticationToken;
	import org.apache.shiro.authc.SimpleAuthenticationInfo;
	import org.apache.shiro.authz.AuthorizationInfo;
	import org.apache.shiro.authz.SimpleAuthorizationInfo;
	import org.apache.shiro.realm.AuthorizingRealm;
	import org.apache.shiro.subject.PrincipalCollection;
	
	import java.util.HashMap;
	import java.util.HashSet;
	import java.util.Map;
	import java.util.Set;
	
	public class MyRealm extends AuthorizingRealm {

    /**
     * 模拟数据库数据
     */
    Map<String, String> userMap = new HashMap<>();

    // 构造代码块,再类进行实例的时候开始填充数据,并设置名称
    {
        userMap.put("username", "123456");
        super.setName("muRealm");
    }

    /**
     * 模拟从数据库中获取权限数据
     * @param userName 要查询权限的用户名
     * @return  返回查询到的用户权限
     */
    private Set<String> getPermissionByUserName(String userName){
        Set<String> permissions = new HashSet<>();
        permissions.add("user:add");
        permissions.add("user:delete");
        return permissions;
    }

    /**
     * 模拟从数据库中获取角色数据
     * @param userName 要查询角色的用户名
     * @return  返回查询到的用户角色
     */
    private Set<String> getRolesByUsername(String userName) {
        Set<String> roles = new HashSet<>();
        roles.add("admin");
        roles.add("user");
        return roles;
    }

    /**
     * 模拟从数据库中获取凭证
     * @param userName 要查询的用户名
     * @return 查询到的用户密码
     */
    private String getPasswordByUserName(String userName){
        return userMap.get(userName);
    }

    /**
     *  授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String)principals.getPrimaryPrincipal();
        // 模拟从数据库获取角色和权限数据
        Set<String> roles = getRolesByUsername(userName);
        Set<String> permissions = getPermissionByUserName(userName);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setStringPermissions(permissions);
        simpleAuthorizationInfo.setRoles(roles);
        return simpleAuthorizationInfo;
    }

    /**
     *
     * @param token 主体传过来的认证信息
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 从主体传过来的认证信息中,获得用户名
        String userName = (String)token.getPrincipal();
        // 通过主体从数据库中获取凭证
        String password = getPasswordByUserName(userName);
        if (password == null) {
            return null;
        }
        return new SimpleAuthenticationInfo(userName, password, "myRealm");
     }
}

写完后再编写测试类TestRealm

import org.apache.shiro.SecurityUtils;
	import org.apache.shiro.authc.UsernamePasswordToken;
	import org.apache.shiro.mgt.DefaultSecurityManager;
	import org.apache.shiro.subject.Subject;
	import org.junit.Test;
	import realm.MyRealm;
	
	public class TestRealm {
	    @Test
	    public void testRealm(){
	        MyRealm myRealm = new MyRealm();
	
	        // 构建securityManager环境
	        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
	        defaultSecurityManager.setRealm(myRealm);
	
	        // 主体提交认证请求
	        SecurityUtils.setSecurityManager(defaultSecurityManager);
	        Subject subject = SecurityUtils.getSubject();
	
	        UsernamePasswordToken token = new UsernamePasswordToken("username", "123456");
	        subject.login(token);
	        if (subject.isAuthenticated()) {
	            System.out.println("登陆成功");
	        }
	        // 判断subject是否具有admin和user两个角色权限,如没有则会报错
	        subject.checkRoles("admin", "user");
			//subject.checkRole("xxx"); // 报错
	        // 判断subject是否具有user:add权限
	        subject.checkPermission("user:add");
	    }
	}

编写完成后可以Run一下,完美运行。

加密

这是一个很重要的模块,因为涉及到了数据安全这个问题。对于用户来说,数据往往是一个特别重要的东西。例如再一个很极端的情况下,发生了数据库数据泄露的问题,如果你存在数据库里的数据没有加密过,那么被泄露出去的数据很容易就会被心术不正的人给利用到,
操作一些真实用户不想操作的功能。

当这个时候数据加密这个功能就体现的重要了起来。如果被泄露出去的数据被加密过呢?那之前所说的问题就迎刃而解了,因为泄露出去的数据是已经加密过的数据,即使别人拿到了这些,他们也无法知道加密前的数据是什么。所以就无法来盗用客户的信息。(采用不可逆的加密算法)

String password = "password";
        String encodePassword = new Md5Hash(password).toString();
        System.out.println(encodePassword);

这样的一串简单的代码就可以实现使用MD5的方式加密数据。

虽然无法直接通过计算反推回密码,但是我们仍然可以通过计算一些简单的密码加密后的 Md5 值进行比较,推算出原来的密码,这个时候密码也就没有我们想象的那么安全。

加盐+多次加密

既然相同密码的md5值相同,那我们就可以在原密码的后面加一个随机数,然后再用md5来加密,这个过程中的添加随机数就叫做加盐。这样的一个处理,就可以让产生的md5值与原密码的md5值不相同了。当然,这个产生的随机数盐,也需要存储到数据库中,方便于以后的验证。
加盐加密流程图
另外也可以使用多次加密的方式,毕竟别人也不知道加密了几次~~
Shiro提供了如下的简化代码方式

String password = "password";
	       String salt = new SecureRandomNumberGenerator().nextBytes().toString();
	       int times = 2; // 加密次数
	       String algorithmName = "md5";
        String encodePassword = new SimpleHash(algorithmName, password, salt, times).toString();
        System.out.printf("原始密码是%s ,盐是%s , 运算次数是%d ,加密后的密码为%s",password,salt,times,encodePassword);

Shiro简单的入门学习就这些了。等这几天继续总结一下在web中使用在继续学习了。

第一次撰写博客,感觉学习的效率也比之前好很多,以后会尽可能的以这种方式学习,为了自己,也为了有可能会读到这篇文章的读者。

最后感谢网络上有这么多技术大神个后辈们总结的知识,学习来才能更加简单,也欢迎大家指出文章中可能出现的各种问题。

参考资料
Shiro安全框架快速入门
Shiro系列教材

欢迎转载,记得标明出处。
GitHub:Cheng2001
知乎:Eweee

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值