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。

   這樣就可以使用啦。這裏沒有列出對用戶名和消息生成消息摘要的類,基本上就是對屬性加密的一個類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章