• Shiro教程之四 散列算法和凭证配置


    Shiro认证密码一般都需要加密(大部分是md5或hash散列加密)

    如果改为密文验证,需要 1.改动一下自定义的Realm类和 2.测试时new出来的 Realm类加入认证的凭证

    先来下加密的例子

        //五,测试加密 (要引入Shiro包,对应的有此类)
        @Test
        public void testMd5Hash(){
            String source = "123456";//原文
            String salt = "abc";//混淆(可选参数)
            int num = 2;//散列加密次数(可选参数)
    
            Md5Hash hash1 = new Md5Hash(source,salt,num);
            System.out.println("123456混淆abc散列2次的结果:"+hash1);
    
        }

    1.改动一下自定义的Realm类

    package com.cc8w.shiro;
    
    
    import com.cc8w.entity.UserActivePojo;
    import com.cc8w.entity.UserPojo;
    import com.cc8w.service.PermssionService;
    import com.cc8w.service.RoleService;
    import com.cc8w.service.UserService;
    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 org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    
    /**
     * Shiro默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。
     * 最基础的是Realm接口,CachingRealm负责缓存处理,AuthenticationRealm负责认证,AuthorizingRealm负责授权,通常自定义的realm继承AuthorizingRealm
     */
    @Component
    public class ShiroRealm extends AuthorizingRealm {
    
        @Autowired
        private UserService userService;
        @Autowired
        private RoleService roleService;
        @Autowired
        private PermssionService permssionService;
        @Override
        public String getName() {
            return this.getClass().getSimpleName();
        }
    
    
    
        /*
         * 登录信息和用户验证信息验证(non-Javadoc)
         * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //1.从authenticationToken中获取身份信息,其实就是用户的登录名
            String username = authenticationToken.getPrincipal().toString();
            String password = authenticationToken.getCredentials().toString();
    
            //2.根据用户名查询用户是否存在
            UserPojo user=userService.queryUserByUserName(username);
            System.out.println(user);
            //返回null说明用户不存在
            if(null!=user) {
                //2.1根据用户名去查询用户拥有哪些角色
                List<String> roles= roleService.queryRolesByUserName(user.getUserName());
                System.out.println(roles);
                //2.2根据用户名查询用户拥有哪些权限
                List<String> permissions=permssionService.queryPermissionsByUserName(user.getUserName());
    
                UserActivePojo activeUser=new UserActivePojo(user, roles, permissions);
    
               //3.返回认证信息(第一个不支持散列加密,第二个可以散列加密)
                /**
                 * 参数1  用户身份
                 * 参数2 用户在数据库里面存放的密码
                 * 参数3 当前类名
                 */
                //SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName());
    
                /**
                 * 参数1 传到doGetAuthorizationInfo里面的getPrimaryPrincipal()对象或subject.getPrincipal()-我们封装的实体activeUser
                 * 参数2 hashedCredentials加密后的密码(数据库查询的密码)-user.getPassword()
                 * 参数3 混淆(盐)
                 * 参数4 当前类名
                 */
                SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName)
                return info;
    
            }
            return null;
        }
    
        /*
         * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
         * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //1.获得用户身份信息(PrincipalCollection有认证回调传来的第一个参数[activeUser])
            UserActivePojo activeUser = (UserActivePojo) principalCollection.getPrimaryPrincipal();
            System.out.println("doGetAuthorizationInfo");
            //2.根据身份信息获取权限数据
            SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
    
            //3.根据用户查询用户的角色 (其实认证方法一并查询出来了,保存在UserActivePojo)
            List<String> roles = activeUser.getRoles();
            if(null!=roles&&roles.size()>0) {
                info.addRoles(roles);//添加角色
            }
            //4.根据用户查询用户的权限
            List<String> permissions=activeUser.getPermissions();
            if(null!=permissions&&permissions.size()>0) {
                info.addStringPermissions(permissions);//添加权限
            }
    
            /**
             * 总结:本来授权->查角色和权限都在本方法写(但是前端每查询一次权限,就会回调本方法一次,
             * 所以直接查数据库,对数据库有压力),所以最后,
             * 1.把查角色和权限方法写在了认证,然后封装成activeUser传递过来,这样Controller每次查权限,就不用查数据库了,直接在activeUser获取即可.
             * 2.缓存应该也可以解决
             */
    
    
            return info;
        }
    
    
    }

    2.测试时new出来的 Realm类加入认证的凭证

    <和上几篇文章一样,最重要的改动是 new Realm(),之后加入了散列密码相关的设置>

    package com.cc8w.test;
    
    
    
    import com.cc8w.shiro.ShiroRealm;
    import org.apache.log4j.Logger;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.authz.AuthorizationException;
    import org.apache.shiro.authz.UnauthenticatedException;
    import org.apache.shiro.authz.UnauthorizedException;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import java.util.Arrays;
    
    /**
     *
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
    public class TestShiro {
        private static Logger logger = Logger.getLogger(TestShiro.class);
    
        @Autowired
        private ShiroRealm shiroRealm;
    
        public static void main(String[] args) {
            TestShiro ts = new TestShiro();
            ts.testAuth();
    
    
        }
    
        //三,登录测试(自定义Realm)
        @Test
        public void testRealmLogin(){
            //1.创建一个安全管理器的工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory();
            //2.在工厂中获取安全管理器
            DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance();
    
            //---添加认证凭证配置start  --如果Spring环境下(这些都是在xml配置的)
            //2.1 创建自定义Realm注入到安全管理器
            ShiroRealm shiroRealm = new ShiroRealm();//(SpringM在bean控制,并可以配置散列加密相关)
            //2.1.1设置密码学相关加密方式
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            //2.1.2设置加密方式
            credentialsMatcher.setHashAlgorithmName("md5");
            //2.1.3设置散列次数
            credentialsMatcher.setHashIterations(1);
            //2.1.4将密码学凭证注入到自定义的Realm类中
            shiroRealm.setCredentialsMatcher(credentialsMatcher);
            //---添加认证凭证配置end  --如果Spring环境下(这些都是在xml配置的)
    
            securityManager.setRealm(shiroRealm);
    
            //3.将securityManager绑定到运行环境
            SecurityUtils.setSecurityManager(securityManager);
            //4.获取Subject对象(将要登录的用户)
            Subject subject = SecurityUtils.getSubject();
            //5.获取要登录用户的token,客户端传递过来的用户名和密码
            String username = "zhangsan",password="123456";
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    
            try{
                //6.登陆(认证)
                subject.login(token);
                logger.info("登录了");
    
            }catch (IncorrectCredentialsException  e ){
                logger.info("密码不正确");
                logger.info(e);
            }catch (UnknownAccountException e) {
                System.out.println("没有这个帐号");
            }catch (AuthenticationException e) {
                e.printStackTrace();
            }
    
            //如果登录成功了,可以获取subject中各种状态了
            Boolean isAuth = subject.isAuthenticated();
            System.out.println("认证状态:" + isAuth);
    
            // 7.授权 分为:基于角色授权 基于资源的授权
            //7.1 基于角色授权
            boolean permited  = subject.hasRole("role1");
            System.out.println("这是授权单个:"+permited);
            boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2","role3"));
            System.out.println("这个授权多个"+hasAllRoles);
    
            // 使用check方法进行授权,如果授权不通过会抛出异常
            // subject.checkRole("role13");
            try {
                subject.checkRole("roles1");
            }catch (UnauthenticatedException e){
                logger.info("没有这个角色");
                //e.printStackTrace();
            }catch (UnauthorizedException e){
                logger.info("没有这个权限");
                //e.printStackTrace();
            }
    
    
            //7.2 基于资源的授权
            //isPermitted传入权限标识符
            boolean isPermitted = subject.isPermitted("user:query");
            System.out.println("单个权限判断:"+isPermitted);
    
            boolean isPermittedAll = subject.isPermittedAll("user:query","user:adb","user:add");
            System.out.println("多个权限判断"+isPermittedAll);
    
            // 使用check方法进行授权,如果授权不通过会抛出异常
            try {
                subject.checkPermission("user:adb");
            }catch (UnauthenticatedException e){
                logger.info("没有这个角色");
                //e.printStackTrace();
            }catch (UnauthorizedException e){
                logger.info("没有这个权限");
                //e.printStackTrace();
            }
    
    
    
    
    
        }
    
        //四,授权验证(自定义Realm)
        public void testRealmAuth(){
            //其实授权也需要登陆(上面方法第7条之后:就是授权的验证)
    
        }
    
    
    
    }
  • 相关阅读:
    leetcode 之 Palindrome Partitioning
    虚拟机共享文件夹下tar
    leetcode 之 Excel Sheet Column Number
    AndroidHttpClient & jsoup 解析 正方教务系统
    查看android下的分区表
    ubuntukylin 下编译 android4.4
    android:layout_weight 和 android: weightSum的使用
    Intent 与Bundle的传值关系
    使用自定义的Toast
    SlideMenu例子解析2
  • 原文地址:https://www.cnblogs.com/fps2tao/p/13572489.html
Copyright © 2020-2023  润新知