Широ добавляет соль для аутентификации/хранения информации о пользователе

Shiro

предисловие

Spring Boot интегрирует Shiro для необходимой предварительной работы, такой как аутентификация личности.Вы можете искать в Интернете.Эта статья в основном посвящена пользовательской аутентификации шифрования для аутентификации пользователя при входе в систему, и Shiro сохраняет содержимое пользователей, вошедших в систему, для последующего использования.

1.1 Вход по электронной почте пользователя

Примечание. Когда пользователь настраивает адрес для входа в Широ, фактическая реализация URL-адреса запроса логики проверки входа должна быть установлена ​​на отсутствие проверки (новичок).

ShiroConfig.java

 /**
   * Shiro Filter
   *
   * @param securityManager securityManager
   * @return shiroFilterFactoryBean
   */
  @Bean
  public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
    log.info("======== Shiro config ==========");

    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);

    // jwt过滤器
    Map<String, Filter> filterMap = shiroFilterFactoryBean.getFilters();
    filterMap.put("jwt", new JWTFilter());
    shiroFilterFactoryBean.setFilters(filterMap);
    shiroFilterFactoryBean.setUnauthorizedUrl("/401");
    // 设置登录路径
    shiroFilterFactoryBean.setLoginUrl("/login");
    // 拦截器
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    // 实际登录地址,不能为/login
    filterChainDefinitionMap.put("/doLogin", "anon");
    filterChainDefinitionMap.put("/login", "anon");
    ......
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return shiroFilterFactoryBean;
  }

Электронная почта пользователя для входа: электронная почта в качестве учетной записи;

LoginController.java

/**
 * login
 *
 * @param email 登录邮箱
 * @param password  登录密码
 * @return login
 */
@PostMapping("/doLogin")
public String login(String email, String password) {
    // 创建Subject实例
    Subject subject = SecurityUtils.getSubject();
    // 封装用户数据
    UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(email, password);
    // 登录判断
    try {
        subject.login(usernamePasswordToken);
        if (subject.isAuthenticated()) {
            return "redirect:/customers";
        }
    } catch (UnknownAccountException e) {
        log.info("---> {}登录失败", email);
    }

    return "login";
    }

1.2 Пароль пользователя плюс проверка соли

Примечание. Shiro сама поддерживает проверку шифрования MD5, используйте HashedCredentialsMatcher для настройки правил шифрования для шифрования.

/**
 * 这里需要设置成与PasswordEncrypter类相同的加密规则
 *
 * 在doGetAuthenticationInfo认证登陆返回SimpleAuthenticationInfo时会使用hashedCredentialsMatcher
 * 把用户填入密码加密后生成散列码与数据库对应的散列码进行对比
 *
 * HashedCredentialsMatcher会自动根据AuthenticationInfo的类型是否是SaltedAuthenticationInfo来获取credentialsSalt盐
 *
 * @return
 */
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法, 与注册时使用的散列算法相同
    hashedCredentialsMatcher.setHashIterations(2);// 散列次数, 与注册时使用的散列册数相同
    hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);// 生成16进制, 与注册时的生成格式相同
    return hashedCredentialsMatcher;
}

Однако он не использует собственный метод шифрования для зашифрованного пользователем хранилища, поэтому необходимо переписать метод setCredentialsMatcher в Realm, чтобы обеспечить единство (кастомизацию) шифрования и проверки.Я выбираю переписать метод вместо пользовательского шифрования , Класс проверки. (Проверьте код класса Realm ниже)

AuthRealmForWeb.java

/**
 * 校验用户身份
 *
 * @param auth
 * @return
 * @throws AuthenticationException
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
    String email = (String) auth.getPrincipal();

    Optional<User> userOptional = userService.findByEmail(email);
    if (!userOptional.isPresent()) {
        throw new UnknownAccountException("未查询到该用户信息");
    }
    User user = userOptional.get();

    // 此处第一个参数传递user,则将登录user信息存储备用
    // 如果使用加盐验证,则第三个参数必须使用ByteSource.Util.bytes(xxx)
    SimpleAuthenticationInfo authenticationInfo =
            new SimpleAuthenticationInfo(user, user.getPassword(),
                    ByteSource.Util.bytes(user.getSalt()), getName());

    return authenticationInfo;
}

/**
 * Authorizaton 授权
 *
 * @param principals
 * @return
 */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    User user = (User) principals.getPrimaryPrincipal();

    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

    // 赋予登录用户权限
    Optional<List<Role>> optionalRoleList = userRoleService.findRolesByUserId(user.getId());
    if (optionalRoleList.isPresent()) {
        // 授权
        for (Role role : optionalRoleList.get()) {
            authorizationInfo.addRole(role.getName());
            Optional<List<Permission>> optionalPermissions = rolePermissionService.findPermissionsByRoleIds(
                    Arrays.asList(role.getId()));
            if (optionalPermissions.isPresent()) {
                authorizationInfo.addStringPermissions(optionalPermissions.get()
                        .stream().map(Permission::getPval).collect(Collectors.toList()));
            }
        }
    }

    return authorizationInfo;
}

 @Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
    credentialsMatcher = (token, info) -> {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        // 验证时传递的加密盐
        String salt = new String(((SimpleAuthenticationInfo) info).getCredentialsSalt().getBytes());
        // 登录录入的密码
        String password = new String(usernamePasswordToken.getPassword());
        // 自定义的加盐加密方式
        String realPassword = Hashing.sha512().hashString(password + salt,
                Charsets.UTF_8).toString().substring(0, 17);

        return realPassword.equalsIgnoreCase(info.getCredentials().toString());
    };
    super.setCredentialsMatcher(credentialsMatcher);
}

1.3 Хранить информацию о пользователе

Сохраните информацию о пользователе, как показано в предыдущем разделе 1.2, просто установите первый параметр для текущего пользователя:

// 此处第一个参数传递user,则将登录user信息存储备用
    // 如果使用加盐验证,则第三个参数必须使用ByteSource.Util.bytes(xxx)
    SimpleAuthenticationInfo authenticationInfo =
            new SimpleAuthenticationInfo(user, user.getPassword(),
                    ByteSource.Util.bytes(user.getSalt()), getName());

После сохранения информации о пользователе, как получить сохраненную информацию о пользователе? Очень просто, он хранится в Теме следующим образом:

User currentUser = (User) SecurityUtils.getSubject().getPrincipal();

Суммировать

Shrio слышал о нем, но сам не пользовался, а просто использовал в проекте, чтобы сделать сюрприз.Обнаружил, что статьи в интернете не подходят для моих нужд, а соответствующего кода нет; эта статья сочетается с реальными потребностями моего собственного проекта, записываю ее и имею возможность обновить ее позже, связанную с Сиро, для углубления понимания;

Если у вас есть какие-либо вопросы или сомнения в тексте, пожалуйста, поправьте меня!