shiro(入门一)详细简单的认证(Realm详解 )

1.Shiro简介

ApacheShiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理。
Shiro有三大核心组件:

  1. Subject :当前用户的操作。即当前用户,在权限管理的应用程序里往往需要知道谁能够操作什么,谁拥有操作该程序的权利,shiro中则需要通过Subject来提供基础的当前用户信息,Subject 不仅仅代表某个用户,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物
  2. SecurityManager:用于管理所有的Subject。即所有Subject的管理者,这是Shiro框架的核心组件,可以把他看做是一个Shiro框架的全局管理组件,用于调度各种Shiro框架的服务。
  3. Realms:用于进行权限信息的验证。Realms则是用户的信息认证器和用户的权限人证器,我们需要自己来实现Realms来自定义的管理我们自己系统内部的权限规则。
    Shiro官方架构图片:
    在这里插入图片描述

其中Authentication 和 Authorization 在shiro的用户权限认证过程中其通过两个方法来实现:

1、Authentication:是验证用户身份的过程。

2、Authorization:是授权访问控制,用于对用户进行的操作进行人证授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

除了以上几个组件外,Shiro还有几个其他组件:

1、SessionManager :Shiro为任何应用提供了一个会话编程范式。

2、CacheManager :对Shiro的其他组件提供缓存支持。

2. Shrio认证

我们来看几个简单的demo
demo路径:
在这里插入图片描述
使用的maven测试的 maven需要的依赖:

<dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.41</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.29</version>
        </dependency>


    </dependencies>

3.使用simpleAccountRealm 进行认证

package com.imooc.test;

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("Mark", "123456", "admin", "user");
    }
    @Test
    public void testAuthentication() {
        //1.构建SecurityManager环境
        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.checkRole("admin");
        subject.checkRoles("admin", "user");
        //退出
//        subject.logout();
//        System.out.println("isAuthenticated:" + subject.isAuthenticated());
    }
}

4.使用IniRealm 进行认证

package com.imooc.test;

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

public class IniRealmTest {
    IniRealm iniRealm = new IniRealm("classpath:user.ini");

    @Test
    public void testAuthentication() {
        //1.构建SecurityManager环境
        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:update");
        //退出
    }
}

user.ini:

[users]
Mark=123456,admin
[roles]
admin=user:delete,user:update

5.使用JdbcRealm进行认证

package com.imooc.test;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

public class JdbcRealmTest {
    DruidDataSource dataSource = new DruidDataSource();

    {
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");

    }

    @Test
    public void testAuthentication() {
        JdbcRealm jdbcRealm = new JdbcRealm();
        jdbcRealm.setDataSource(dataSource);
        //设置权限开关,默认为false
        jdbcRealm.setPermissionsLookupEnabled(true);

        String sql = "select password from test_user where user_name = ?";
        jdbcRealm.setAuthenticationQuery(sql);


        String rolesql = "select role_name from test_user_role where user_name = ?";
        jdbcRealm.setUserRolesQuery(rolesql);

        //1.构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(jdbcRealm);
        //2主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);//设置环境
        Subject subject = SecurityUtils.getSubject();//获得主体
        UsernamePasswordToken token = new UsernamePasswordToken("xiaoming", "654321");
        subject.login(token);

        System.out.println("isAuthenticated:" + subject.isAuthenticated());
        //是否具备管理员角色
//        subject.checkRole("admin");
//        subject.checkRoles("admin", "user");
//
//        subject.checkPermission("user:select");

        subject.checkRole("user");
    }
}

使用JdbcRealm值得注意的是可以不设置jdbcRealm.setAuthenticationQuery(sql)jdbcRealm.setAuthenticationQuery(sql);
这里就有问题我们不设置SQL也能认证呢,
我们看看JdbcRealm的源码发现:如果不设置SQL语句,他是有自己的查询语句的,不过我们要使用默认的sql需要数据库的表结构和字段与默认sql中的表字段同步
在这里插入图片描述
我的sql表结构字段如下:
users:
在这里插入图片描述
user_roles:
在这里插入图片描述
roles_permissions
在这里插入图片描述

6. 使用自定义Realm认证

使用自定义Realm需要继承AuthorizingRealm,并重写两个方法:
doGetAuthorizationInfo(授权)doGetAuthenticationInfo(认证)

package com.imooc.shiro.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 CustomRealm extends AuthorizingRealm {
	//模拟数据库
    Map<String, String> userMap = new HashMap<String, String>(16);

    {
        userMap.put("Mark", "e10adc3949ba59abbe56e057f20f883e");//原密码为:123456,因为使用了MD5
        加密
        super.setName("customRealm");
    }

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取用户
        String userName = (String) principalCollection.getPrimaryPrincipal();
        //从数据库或者缓存中获取角色数据
        Set<String> roles = getRolesByUserName(userName);
		//从数据库或者缓存中获取权限数据
        Set<String> permissions = getPermissionByUserName(userName);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //设置权限
        simpleAuthorizationInfo.setStringPermissions(permissions);
        //设置角色
        simpleAuthorizationInfo.setRoles(roles);

        return simpleAuthorizationInfo;
    }

    private Set<String> getPermissionByUserName(String userName) {
        Set<String> sets = new HashSet<String>();
        sets.add("user:delete");
        sets.add("user:add");
        return sets;
    }

    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 {
        // 1. 从主体传过来的认证信息中,获得用户名
        String userName = (String) authenticationToken.getPrincipal();

        // 2. 通过用户名到数据库获取凭证
        String password = getpasswordByUserName(userName);
        if (null == password) {
            return  null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("Mark", password, "customRealm");
        //设置盐
        authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("zou"));
        return authenticationInfo;
    }

    /*
    * 模拟数据库查询凭证(获得密码)
    * */
    private String getpasswordByUserName(String userName) {
        return userMap.get(userName);
    }
}

CustomRealmTest测试:

package com.imooc.test;

import com.imooc.shiro.realm.CustomRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

public class CustomRealmTest {

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

        // 2.声明CustomRealm使用了Md5加密
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //加密算法为MD5
        matcher.setHashAlgorithmName("md5");
        //加密次数为1
        matcher.setHashIterations(1);
        customRealm.setCredentialsMatcher(matcher);
        
        //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.checkPermissions("user:add", "user:delete");
        //退出
    }
    //用来计算123456加密后的密码为:e10adc3949ba59abbe56e057f20f883e
    public static void main(String[] args) {
    	//盐为zou  一般为随机数,这里为了方便
        Md5Hash md5Hash = new Md5Hash("123456", "zou");
        System.out.println(md5Hash);
    }
}


猜你喜欢

转载自blog.csdn.net/qq_42651904/article/details/87968384