菜单

SpringBoot 整合 Shiro

duckflew
发布于 2021-07-08 / 318 阅读
0
0

SpringBoot 整合 Shiro

SpringBoot 整合 Shiro

### 依赖
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.7.1</version>
</dependency>

注入shiro核心过滤器

Shiro与Springboot整合有个核心过滤器 用来拦截所有请求 名字叫做ShiroFilter 但是一般注入的是它的子类 通过工厂格式拿到

@Bean
public ShiroFilterFactoryBean getShiroFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager)
{
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
    //修改默认 认证页面
    shiroFilterFactoryBean.setLoginUrl("/login.html");
    //页面保护相关信息
    Map<String,String> map=new HashMap<>();
    map.put("/hello","authc");
    map.put("/user/*","anon");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

    return shiroFilterFactoryBean ;
}

当然了 我们知道 shiro的 核心认证授权功能都是通过SecurityManager实现的 所以在构造方法中注入 ShiroFilter 主要配置的有如下内容

  • 修改默认的认证页面 这里设置为login.html 默认的是 login.jsp
  • 然后就是授权相关的信息了 这里通过一个map来保存相关配置 控制哪些资源需要什么样的权限来访问

注入shiro安全管理器

@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm)
{
    DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
    defaultWebSecurityManager.setRealm(realm);
    return defaultWebSecurityManager;
}

安全管理器没有什么好配置的 主要是设置一下自定义的Realm 这个Realm实现了认证和授权相关的具体 逻辑

Realm

自定义Realm

通过继承AuthorizingRealm 重写do认证和do授权两个方法进行

package cn.duckflew.demo.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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class CustomerRealm extends AuthorizingRealm
{
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
    {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
    {
        System.out.println("开始认证");
        String principal = (String) authenticationToken.getPrincipal();
        if (principal.equals("xiaochen"))
        {
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("xiaochen", "9c074aff230a802bf52901cddd5c81da", ByteSource.Util.bytes("salt"), this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}
  • 其中 AuthenticationInfo是认证相关的信息 如果查询到有这个用户 则返回相应的 对象进行下一步的密码校验

    如果查不到用户这里就返回空表示没有这个用户 会抛出 UnknownAccount异常

  • AuthorizationInfo是授权相关的信息 返回一个用户信息+权限相关的类 表示这个用户具有哪些权限 其中的参数是用户的身份集合 principalCollection 一个用户可以有很多个身份 例如手机号 邮箱 用户名等等 但是只能有一个主要的身份 这个叫做prinmaryPrincipal 可以通过这个集合拿到 拿到用户主身份 查询数据库 得到用户相关的权限信息

注入Realm
@Primary
@Bean
public Realm getRealm()
{

    CustomerRealm realm = new CustomerRealm();
    /**
     * 新建凭证管理器
     */
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher("md5");
    hashedCredentialsMatcher.setHashIterations(1024);   //设置散列次数
    realm.setCredentialsMatcher(hashedCredentialsMatcher);//注入凭证匹配器
    return realm;
}

开始认证的主要步骤

public String login(String username, String password)
{
    UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
    Subject subject = SecurityUtils.getSubject();
    try
    {
        subject.login(usernamePasswordToken);
        return "认证成功"+username;
    } catch (IncorrectCredentialsException e)
    {
        return "密码错误";
    }
    catch (UnknownAccountException e)
    {
        return "用户名错误";
    }
}

由于我们在Spring容器已经注入了安全管理 SecurityUtils会自动帮我们注入 所以可以直接获取到Subject 我们对这个subject.login 方法

进入认证流程 根据前端传来的账号密码相关信息 封装一个UsernamePasswordToken传入login中 这个类是AuthenticationToken的子类 login方法需要的参数也是这个类

以上就是认证的主要流程 此外考虑到用户的密码安全问题 密码采用md5+salt 再加 多次哈希散列化 的方法 数据库只保存用户的密文密码 并把盐保存在密码中(虽然黑客能拿到数据库密码 并且也能拿到盐 但是这并不代表加盐是无用的 因为加盐的策略是由我们来规定的 可以选择加明文尾部或者首部 或者任意第几个字符之间 这增加了黑客的碰撞成本

这样设置了密文之后 我们需要在shrio认证的相关配置里面增加 一些相关的配置 来使用shiro进行密文匹配 因为默认的匹配规则是根据明文匹配的

我们需要自定义一个密文匹配器 也叫凭证匹配器

这个在构造Realm的时候我们已经构造了

@Primary
@Bean
public Realm getRealm()
{

    CustomerRealm realm = new CustomerRealm();
    /**
    * 新建凭证管理器
    */
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher("md5");
    hashedCredentialsMatcher.setHashIterations(1024);   //设置散列次数
    realm.setCredentialsMatcher(hashedCredentialsMatcher);//注入凭证匹配器
    return realm;
}

评论