美文网首页
shiro在springboot的使用及思考

shiro在springboot的使用及思考

作者: 被咬过的馒头 | 来源:发表于2020-04-24 15:54 被阅读0次
shiro在springboot中的使用
1,配置shiro
@Configuration
public class ShiroConfig {
    /*
    1,创建过滤器,加入自定义的安全管理器
    2,loginurl在未登入的用户访问需要登入才能访问的网页时自动跳转,unauthorized同理,successurl用于成功访问后自动跳转的页面
       (失效的原因为successurl会根据session中存储的信息进行跳转,如果请求被拦截session只会记住被拦截前的url进行跳转
        解决方案:重写FormAuthenticationFilter中的issueSuccessRedirect方法将successurl固定)
    3,权限控制通过map对路径控制
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager")SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();
        shiroFilterFactoryBean.setLoginUrl("/admin/login");//登入页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/admin/unauthc");//没授权的页面
        shiroFilterFactoryBean.setSuccessUrl("/admin/successlogin");//登入成功的页面

        filterChainDefinitionMap.put("/*", "anon");//所有用户都能访问
        filterChainDefinitionMap.put("/admin/adminhome", "roles[admin]");//所有登录用户都能访问
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    //自定义的加密方式
    @Bean
    public PasswordHelper passwordHelper() {
        return new PasswordHelper();
    }

    //创建匹配器用于转化用户输入的密码
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(PasswordHelper.ALGORITHM_NAME); // 散列算法
        hashedCredentialsMatcher.setHashIterations(PasswordHelper.HASH_ITERATIONS); // 散列次数
        return hashedCredentialsMatcher;
    }

    //重写授权域并加入自定义的匹配器
    @Bean(name="shiroRealm")
    public CustomizeShiroRealm shiroRealm() {
        CustomizeShiroRealm shiroRealm = new CustomizeShiroRealm();
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); // 原来在这里
        return shiroRealm;
    }

    //创建安全管理器,setrealm是必须操作的,在登入操作中ModularRealmAuthenticator类会对是否设置realm进行assert判断
    @Bean(name = "securityManager")
    public SecurityManager securityManager(@Qualifier("shiroRealm")CustomizeShiroRealm shiroRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm);
        return securityManager;
    }
}

2,加密器
public class PasswordHelper {
    private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
    public static final String ALGORITHM_NAME = "md5"; // 基础散列算法
    public static final int HASH_ITERATIONS = 2; // 自定义散列次数

    /*
    作用:在注册的时候为用户生成随机盐值以以及利用盐值进行加密
    1,bytesource.util.bytes转换为的类似byte数组是为了后期比对密码的用的,
    2,转换为hex是为了存储,因为在利用密码匹配器匹配密码时数据库中的密码都会转换为byte数组
     */
    public void encryptPassword(User user) {
        // 随机字符串作为salt因子
        user.setSalt(randomNumberGenerator.nextBytes().toHex());
        String newPassword = new SimpleHash(ALGORITHM_NAME, user.getPassword(),
                ByteSource.Util.bytes(user.getCredentialsSalt()), HASH_ITERATIONS).toHex();
        user.setPassword(newPassword);
    }
}
3,重写授权域
public class CustomizeShiroRealm extends AuthorizingRealm {
    @Autowired
    private adminService adminservice;

    /*
        作用:获取授权信息,也就是该用户的角色和允许的操作
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String username = (String) principals.getPrimaryPrincipal();

        User user = adminservice.inserUserRolePerm(username);
        authorizationInfo.addRole(user.getRole().getRole());
        for (SysPermission permission : user.getRole().getPermissions()) {
            authorizationInfo.addStringPermission(permission.getName());
        }
        return authorizationInfo;
    }

    /*
        作用:获取用户数据库中的信息
        1,SimpleAuthenticationInfo中password是对比用的,盐值是给输入的密码加密用的
        2,getName的作用是获取当前域的名字,而这个名字是每个用户专属的name=类名+自增整数组成,作用:用来缓存标记,方法所属类CachingRealm
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        User user = adminservice.inserUserRolePerm(username);
        if (user == null) {
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),
                ByteSource.Util.bytes(user.getCredentialsSalt()), getName());
        return authenticationInfo;
    }

}
4,验证信息

问题一:subject代表什么,怎么生成的

subject从表面上来讲代表着用户,subject生成过程

  1. 将全局唯一securityManager注入到subjectcontext中
  2. 利用subjectcontext为参数执行securityManager中的createsubject(该函数目的为了确认安全管理器的注入,解析session(用来存权限),解析Principals(代表登入时存储的对象))
  3. 最后通过调用SubjectFactory构建DelegatingSubject对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
  subject.login(token);
} catch (IncorrectCredentialsException ice) {
  return new adminUnionDto<String>(responsestate.FAILLOGIN.state,responsestate.FAILLOGIN.msg,null);
} catch (UnknownAccountException uae) {
  return new adminUnionDto<String>(responsestate.FAILLOGIN.state,responsestate.FAILLOGIN.msg,null);
}
User user = adminservice.inserUserRolePerm(username);
subject.getSession().setAttribute("user", user);
shiro验证的过程
  1. 验证是否设置realm,根据单realm或多realm选择对于处理方式,调用获取用户信息的函数,在获取用户信息的顺便认证

    if (info != null) {
      this.assertCredentialsMatch(token, info);
    } else {
      log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
    }
    
  1. 开始认证,利用上方设置的CredentialsMatcher来匹配代码为

    //主要作用为:用数据库中的salt加密token中的密码然后用simplehash包装 
    Object tokenHashedCredentials = this.hashProvidedCredentials(token, info);
    //主要作用为:拿到数据库中密码并且转换为bytes然后用SimpleHash包装起来
    Object accountCredentials = this.getCredentials(info);
     return this.equals(tokenHashedCredentials, accountCredentials);
    
  1. 给subject加上是否认证等标签包装好返回
shiro拓展
1,shiro的认证时限

默认过期单位为毫秒

subject.getSession().setTimeout(-1000L)//永不过期
subject.getSession().setTimeout(1800000)//默认的时间
2,缓存拓展

只要在securityManager设置cachemanager和sessionmanager就能使用,具体操作采用shiro-redis开源包,该包的重点为两个,(1)重写了序列化和反序列化方法,(2)可以自定义类加载器


@Bean
public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("127.0.0.1:6379");
        redisManager.setTimeout(0);
        // redisManager.setPassword(password);
        return redisManager;
    }
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

相关文章

网友评论

      本文标题:shiro在springboot的使用及思考

      本文链接:https://www.haomeiwen.com/subject/mipfwhtx.html