1. 程式人生 > >shiro 前後端分離框架 使用者登入解決方案

shiro 前後端分離框架 使用者登入解決方案

介紹

最近公司要原本springmvc+shiro 許可權控制的一個專案,改為前後端分離的,使前端人員能有更多時間來做前端的互動工作,提升使用者體驗。

但是這個問題就來了,原來沒有分離的情況下,shiro 許可權是通過憑證來登入的,現在需要更改為前端ajax提交登入資訊來登入,於是更改了shiro的登入方式,這裡做一個記錄。

@RequestMapping(value="/userLogin",method=RequestMethod.GET)
    @ResponseBody
    public ResultSuperApp superAppLogin(HttpServletRequest request) throws Exception{
        String username = request.getParameter
("username"); String password = request.getParameter("password"); String error="未知錯誤異常"; if(null == username || null == password ){ return ResultSuperApp.getFailureInstance("引數為空", 300); } try { Subject currentUser = SecurityUtils.getSubject
(); UsernamePasswordToken token = new UsernamePasswordToken(username,password, false,request.getRemoteAddr()); currentUser.login(token); Subject subject = SecurityUtils.getSubject(); String sessionId = (String) subject.getSession().getId(); /*//無Redis伺服器,臨時性解決token String Json = new Gson().toJson(token); byte[] encodeBase64 = Base64.encodeBase64(Json.getBytes("UTF-8")); String base64 = new String(encodeBase64);//Base64值*/
ActiveUser activeUser = ShiroUtil.getActiveUser(); return ResultSuperApp.getSuccess(activeUser, "登入成功", 200, sessionId); //SecurityUtils.getSubject().getSession().setTimeout(-1000l); } catch (Exception e) { //根據與異常資訊丟擲對應的異常 if(e.getClass().getName()!=null){ if(UnknownAccountException.class.getName().equals(e.getClass().getName())){ //丟擲賬號不存在異常 error="賬號不存在"; }else if(IncorrectCredentialsException.class.getName().equals(e.getClass().getName())){ // //throw new CustomException("密碼錯誤"); error = "使用者名稱密碼錯誤"; }else{ //密碼錯誤 //throw new CustomException("未知錯誤異常"); error = "未知錯誤異常"; } } return ResultSuperApp.getFailureInstance(error, 499); } }

注意:以上程式碼註釋掉的部分是返回登入的token資訊,之後每次前端請求的時候,引數都需要帶著這個token。
放開的程式碼片段是登入後,返回shiro的sessionid,前端在cookie裡儲存一下這個sessionid,每次請求的時候引數中都需要加上sessionid。
兩種方式都可以使用,但是本人推薦返回sessionid 的方式,因為前端給了sessionid 之後,我們只需要在服務中通過sessionid 獲取shiro的session資訊,然後就能獲取到shiro對應的使用者資訊。而使用token的這種方式的話,每次前端請求過來,後端都需要根據這個token再登入一下。兩者的區別就在這裡。

獲取使用者資訊(根據sessionid)

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import org.springframework.beans.factory.annotation.Autowired;

import com.eaju.bos.dao.mapper.DownloadCenterMapper;
import com.eaju.bos.dao.mapper.SonUserMapper;
import com.eaju.bos.dao.mapper.SysUserMapper;
import com.eaju.bos.entity.SysUser;
import com.eaju.bos.vo.ActiveUser;
import com.eaju.bos.vo.UserCustom;

/**
 * 
 */
public class ShiroUtil {


    /**
     * 
     * <p>description: 獲取ActiveUser並儲存至session中一份</p>
     * @return
     * @date 2016年8月15日 下午3:37:23
     * @author MrDuan
     */
    public static ActiveUser getActiveUser(){
        //從shiro的session中取出activeUser
        Subject subject = SecurityUtils.getSubject();
        //取出身份資訊
        ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
        if(activeUser!=null){
            Session session = subject.getSession();
            ActiveUser user = (ActiveUser) session.getAttribute("user");
            if(user==null){
                session.setAttribute("user", activeUser);
            }
            return activeUser;
        }else{
            return null;
        }
    }

    /**
     * 根據sessionid 獲取使用者資訊
     * @param sessionID
     * @param request
     * @param response
     * @return
     */
    public static ActiveUser getActiveUser(String sessionID,HttpServletRequest request,HttpServletResponse response) throws Exception{
        boolean status = false;
        SessionKey key = new WebSessionKey(sessionID,request,response);
        Session se = SecurityUtils.getSecurityManager().getSession(key);
        Object obj = se.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
        //org.apache.shiro.subject.SimplePrincipalCollection cannot be cast to com.hncxhd.bywl.entity.manual.UserInfo
        SimplePrincipalCollection coll = (SimplePrincipalCollection) obj;
        ActiveUser activeUser = (ActiveUser)coll.getPrimaryPrincipal();

        if(activeUser!=null){
            ActiveUser user = (ActiveUser) se.getAttribute("user");
            if(user==null){
                se.setAttribute("user", activeUser);
            }
            return activeUser;
        }else{
            return null;
        }

    }


}

sessionid 方式 Controller裡獲取使用者資訊

ActiveUser activeUser = null ;
        try {
            activeUser = ShiroUtil.getActiveUser(token, request, response);
        } catch (Exception e1) {
            if(UnknownSessionException.class.getName().equals(e1.getClass().getName())){
                //丟擲使用者獲取失敗異常
                retJsono.put("info", "使用者獲取失敗!");
                retJsono.put("returnCode",1000);
                return retJsono;
            }else{
                retJsono.put("info", "內部錯誤!");
                retJsono.put("returnCode",500);
                return retJsono;
            }
        }

注意: 這裡是舉個例子,獲取方式就是這個樣子,ActiveUser 是你們自己存在shiro session裡的使用者實體,大家自己修改為自己的就可以了。

獲取使用者資訊(token方式,每次請求過來都需要登入一下,不太推薦)

//登入獲取使用者資訊
    private ActiveUser loginToken(HttpServletRequest request) {
        String token = request.getParameter("token");
        Subject currentUser = SecurityUtils.getSubject();
        byte[] decodeBase64 = Base64.decodeBase64(token);
        String stringtoken = new String(decodeBase64);//Base64值
        currentUser.login(new Gson().fromJson(stringtoken, UsernamePasswordToken.class));
        ActiveUser activeUser = ShiroUtil.getActiveUser();
        return activeUser;
    }

為什麼這麼做?
因為前後端分離後,前後端可能會部署在不同的伺服器上面,會跨域,前端每次請求時,都會是一次新的請求,所以前端需要記住一個使用者的標識,每次請求資料都傳給後端。後端才能知道是哪個使用者。

以上均為個人想法,大家有好的意見或者建議,我會修改。