shiro的一点记录(三)

shiro的无状态web集成。所谓无状态就是服务器端无状态,就是不保存会话。一般的会话机制的web应用,都是session机制来保存用户状态。无状态的web应用就是每次请求都带上相应的用户名进行登录。

具体的实践就是:客户端传入秘钥和一个消息作为输入,他们声称相应消息摘要,秘钥是只有客户端和服务端知道的。访问的时候服务端对消息摘要进行验证。

具体的实例如下:

首先我们创建subject的工厂必须是不保存session的:

public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {

    @Override
    public Subject createSubject(SubjectContext context)
    {
        context.setSessionCreationEnabled(false);
        return super.createSubject(context);
    }
}

我们自定义一个无状态的Filter:

public class StatelessAuthcFilter extends AccessControlFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        //客户端生成的消息摘要
        String clientDigest=servletRequest.getParameter("digest");
        //客户端传入的用户身份
        String username=servletRequest.getParameter("username");
        //客户端的参数列表
        String param1=servletRequest.getParameter("param1");
        String param2=servletRequest.getParameter("param2");
        Map<String,String> params=new HashMap<>();
        params.put("param1",param1);
        params.put("param2",param2);
        //生成无状态Token
        StatelessToken token=new StatelessToken(username,params,clientDigest);
        try {
            getSubject(servletRequest,servletResponse).login(token);
        }
        catch (Exception e) {
            e.printStackTrace();
            onLoginFail(servletResponse);
        }
        return false;
    }
    private void onLoginFail(ServletResponse response) throws IOException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        httpResponse.getWriter().write("login error");
    }
}

shiro框架没有提供专门的无状态的token,我们自己定义一个:

public class StatelessToken implements AuthenticationToken {
    private String username;
    private Map<String, ?> params;
    private String clientDigest;

    public StatelessToken(String username,  Map<String, ?> params, String clientDigest) {
        this.username = username;
        this.params = params;
        this.clientDigest = clientDigest;
    }。。。。。此处省略set和get代码

自定义无状态的realm:

public class StatelessRealm extends AuthorizingRealm {
    @Autowired
    private IMememberService memberService;

    @Override
    public boolean supports(AuthenticationToken token) {
        //仅支持StatelessToken类型的Token
        return token instanceof StatelessToken;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String) principalCollection.getPrimaryPrincipal();
        Member user = memberService.findByUsername(username);
       // System.out.println(user);
        if (user != null) {
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            return authorizationInfo;
        } else throw new IncorrectCredentialsException();
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        StatelessToken statelessToken = (StatelessToken) token;
        String username = statelessToken.getUsername();
        String key = getKey(username);//根据用户名获取密钥(和客户端的一样)
        //在服务器端生成客户端参数消息摘要
        String serverDigest = HmacSHA256Utils.digest(key, statelessToken.getParams());
        System.out.println(statelessToken.getClientDigest());
        System.out.println(serverDigest);
        //然后进行客户端消息摘要和服务器端消息摘要的匹配
        return new SimpleAuthenticationInfo(
                username,
                serverDigest,
                getName());
    }
    /**
     * 获取秘钥,此处是硬编码的一个
     *
     * @param username
     * @return
     */
    private String getKey(String username) {

       if("admin".equals(username))
       {
           return "dadadswdewq2ewdwqdwadsadasd";
        }
      return  null;
    }
}

然后进行shiro的配置文件部分内容:

 <!--statelessReealm-->
      <bean id="statelessRealm" class="com.supuy.sps.realm.StatelessRealm">
        <property name="cachingEnabled" value="false"/>
      </bean>
      <!--statelessReealm subject工厂-->
      <bean id="subjectFactory" class="com.supuy.sps.realm.factory.StatelessDefaultSubjectFactory"></bean>

        <!--statelessFilter-->
       <bean id="statelessFilter" class="com.supuy.sps.realm.filter.StatelessAuthcFilter"></bean>

这里的sessionManager的sessionValidationSchedulerEnabled属性一定得设置为false。

这样就可以使用啦。这里没有列出对用户名和消息生成消息摘要的类,基本上就是对属性加密的一个类。

时间: 2024-10-21 04:58:39

shiro的一点记录(三)的相关文章

shiro的一点记录

由于项目的需要,最近开始研究shiro这个框架.shiro是一个安全框架,主要是验证和授权管理,和它类似的有spring security框架,当然,spring security框架更加强大,但是shiro更加灵活(一般小的东西都比较灵活). 对于shiro,网上的资料可谓是少之又少.基本上算是被开涛大魔王的一个<跟我学shiro>垄断,当然这个教程是很全面,楼主也是跟着这个教程一点点学的(没办法,没有别的资料啊,很多看不懂的地方没地方找啊,shiro官网的英文看不懂啊卧槽).虽然过程很痛苦

shiro的一点记录(二)

这次主要写shiro的exception定制,RememberMe和SSL. Exception页面定制 不知道这个属不属于shiro自身的范畴,反正这里用到了,就写上吧,主要是这些Exception都是shiro里面的,所以就在这里写上吧.根据资料显示,要定制错误页面,需要在springMVC配置文件里面配置相应的控制器异常处理.例如: <!--控制器异常处理 --> <bean id="exceptionHandlerExceptionResolver" clas

Apache Shiro Realm 学习记录1

最近几天在学习Apache Shiro......看了一些大神们的教程.....感觉收获不少.....但是毕竟教程也只是指引一下方向....即使是精辟教程,仍然有很多东西都没有说明....所以自己也稍微研究了一下...记录了一下我的研究发现....教程点这里 这篇教程的最后提到了自己去写Realm.....然后给出了4个方法.....但是并没有怎么详细说明.....我想说说我的理解.....(我的理解可能会有很多错误) 我想先说说登陆验证的大致流程....大致...... 从用户那里收集完用户名

Windows API 编程学习记录&lt;三&gt;

恩,开始写API编程的第三节,其实马上要考试了,但是不把这节写完,心里总感觉不舒服啊.写完赶紧去复习啊       在前两节中,我们介绍了Windows API 编程的一些基本概念和一个最基本API函数 MessageBox的使用,在这节中,我们就来正式编写一个Windows的窗口程序. 在具体编写代码之前,我们必须先要了解一下API 编写窗口程序具体的三个基本步骤:             1. 注册窗口类:             2.创建窗口:             3.显示窗口: 恩,

Sql Server删除数据表中重复记录 三种方法

本文介绍了Sql Server数据库中删除数据表中重复记录的方法. [项目]数据库中users表,包含u_name,u_pwd两个字段,其中u_name存在重复项,现在要实现把重复的项删除![分析]1.生成一张临时表new_users,表结构与users表一样:2.对users表按id做一个循环,每从users表中读出一个条记录,判断new_users中是否存在有相同的u_name,如果没有,则把它插入新表:如果已经有了相同的项,则忽略此条记录:3.把users表改为其它的名称,把new_use

Spring Boot学习记录(三)--整合Mybatis

Spring Boot学习记录(三)–整合Mybatis 标签(空格分隔): spring-boot 控制器,视图解析器前面两篇都已弄好,这一篇学习持久层框架整合. 1.数据源配置 数据源使用druid,maven引入相关依赖,包括spring-jdbc依赖,mysql依赖 1.转换问题 配置的过程要学会为什么这样配置,而不是只学会了配置.这里我们可以和以前的配置方式对比: 以前版本 <!--配置数据库连接池Druid--> <bean id="dataSource"

lodop打印控件一点记录

今天初步接触了下打印控件 LODOP实现了自动分页,高度宽度都可以自己设定来分页. 页码,使用LODOP.SET_PRINT_STYLE("ItemType", 2); LODOP.ADD_PRINT_TEXT(0,0,"95%",30,"总页号:第#页/共&页"); 让每个分页都显示页码/总页数,#当前页,$总页数. 每页页眉页脚.使用LODOP.SET_PRINT_STYLE("ItemType", 1); 来让其

oracle 查询当天记录 三种方法效率比较

-- 查询一表中当天生成的数据 -- 原表mobilefrends中的cdate字段上有索引,创建索引语句是:create index mobilefrends_cdate_idx on mobilefrends(cdate); --------------------------------------------------------------------------------------------------------------------- -- 方法一:用to_char()

对Integer类中的私有IntegerCache缓存类的一点记录

对Integer类中的私有IntegerCache缓存类的一点记录 // Integer类有内部缓存,存贮着-128 到 127. // 所以,每个使用这些数字的变量都指向同一个缓存数据 // 因此可以直接使用 == 来比较是否相等 Integer a = 88; Integer b = 88; System.out.println(a == b); // true // 下面这个不在Integer缓存类里的数字,在每次赋值的时候都会新建一个对象存放 // 所以,它们不能使用 == 来判断是否相