基于token身份認(rèn)證的完整實(shí)例

前言

基于Token的身份認(rèn)證是無狀態(tài)的顾翼,服務(wù)器或者Session中不會(huì)存儲(chǔ)任何用戶信息屯伞。

一芜辕、基于Token的身份認(rèn)證工作流程

1.用戶通過用戶名和密碼請(qǐng)求訪問

2.服務(wù)器驗(yàn)證用戶律杠,通過校驗(yàn)則向客戶端返回一個(gè)token

3.客戶端存儲(chǔ)token第岖,并且在隨后的每一次請(qǐng)求中都帶著它

4.服務(wù)器校驗(yàn)token并返回?cái)?shù)據(jù)

每一次請(qǐng)求都需要token -Token應(yīng)該放在請(qǐng)求header中 -我們還需要將服務(wù)器設(shè)置為接受來自所有域的請(qǐng)求难菌,用Access-Control-Allow-Origin: *

二、實(shí)現(xiàn)步驟

1.配置過濾請(qǐng)求

在web-xml中配置需要認(rèn)證的請(qǐng)求

<filter>
        <filter-name>authFilter</filter-name>
        <filter-class>com.demo.filter.AuthFilter</filter-class>
        <init-param>
            <param-name>ignore</param-name>
            <param-value>
                /**
            </param-value>
        </init-param>
        <init-param>
            <param-name>noIgnore</param-name>
            <param-value>
                /**/userApi/**/**
            </param-value>
        </init-param>
    </filter>

2.實(shí)現(xiàn)過濾器

代碼如下(示例):

public class AuthFilter implements Filter {
    protected Pattern[] ignorePattern = null;
    PathMatcher matcher = new AntPathMatcher();
    // 不用過濾的請(qǐng)求
    String[] ignoreStrs = null;
    // 需要過濾的請(qǐng)求
    String[] noIgnoreStrs = null;

    public AuthFilter() {
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        String uri = request.getRequestURI();
        // 判斷是否需要認(rèn)證
        if (this.checkIgnore(uri) && !this.checkNoIgnore(uri)) {
            chain.doFilter(request, response);
        } else {
            // 用戶認(rèn)證
            Author.checkAuthorLocal(request, response, chain);
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        String ignore = config.getInitParameter("ignore");
        String noIgnore = config.getInitParameter("noIgnore");
        ignore = StringUtils.defaultIfEmpty(ignore, "");
        this.ignoreStrs = ignore.replaceAll("\\s", "").split(",");
        this.noIgnoreStrs = noIgnore.replaceAll("\\s", "").split(",");
    }

    public boolean checkIgnore(String requestUrl) {
        boolean flag = false;
        String[] var6 = this.ignoreStrs;
        int var5 = this.ignoreStrs.length;
        for(int var4 = 0; var4 < var5; ++var4) {
            String pattern = var6[var4];
            if (flag = this.matcher.match(pattern, requestUrl)) {
                break;
            }
        }
        return flag;
    }

    public boolean checkNoIgnore(String requestUrl) {
        boolean flag = false;
        String[] var6 = this.noIgnoreStrs;
        int var5 = this.noIgnoreStrs.length;
        for(int var4 = 0; var4 < var5; ++var4) {
            String pattern = var6[var4];
            if (flag = this.matcher.match(pattern, requestUrl)) {
                break;
            }
        }
        return flag;
    }
}

簡(jiǎn)單認(rèn)證的實(shí)現(xiàn)類

public class Author {

    public Author() {
    }

    public static void checkAuthorLocal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (!request.getMethod().equals("OPTIONS")) {
            // 獲取token
            String token = request.getHeader("oauth_token");
            if (!StringUtil.hasText(token)) {
                token = request.getParameter("oauth_token");
            }
            String loginUserId;
            if (StringUtil.hasText(token)) {
                // 判斷token有效性
                loginUserId = getLoginUserId(token);
                if (StringUtil.hasText(loginUserId)) {
                    setLoginUserId(token, loginUserId);
                    chain.doFilter(request, response);
                } else {
                    response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                    PrintWriter writer = response.getWriter();
                    writer.print("no access");
                    return;
                }
            } else {
                response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                PrintWriter writer = response.getWriter();
                writer.print("no access");
                return;
            }
        }
    }

    private static String getLoginUserId(String accessToken) {
        String userId = RedisClient.get(accessToken);
        return userId;
    }

    public static void setLoginUserId(String accessToken, String userId) {
        try {
            RedisClient.set(accessToken, userId, Cluster.getTimeoutSecond());
        } catch (Exception var3) {
            var3.printStackTrace();
        }
    }
}
 if (!request.getMethod().equals("OPTIONS")) {...}

這段代碼主要是解決后端接收不到前端傳入的header參數(shù)信息的問題蔑滓,前面也提過郊酒。

用戶存儲(chǔ)的工具類
代碼如下(示例):

public class AuthSessionContext extends UserSessionContext {

    public static String USER_KEY = "userKey";
    public static String CACHE_KEY_USER_IDENTITY = "USER_IDENTITY_DATA";
    public static String CACHE_KEY_USER_IDENTITY_ID = "USER_IDENTITY_ID";
    //private static ICaheManager RedisClient = CacheManagerFactory.getCentralizedCacheManager();


    public static UserSessionContextService getContextService() {
        return (UserSessionContextService) BeanManager.getBean(UserSessionContextService.class);
    }

    /** @deprecated */
    @Deprecated
    public static String getLoginPersonKey(HttpServletRequest request) {
        return getLoginUserId(request);
    }

    public static String getLoginIdentityId(HttpServletRequest request) {
        String loginUserId = getLoginUserId(request);
        String loginIdentityId = (String) RedisClient.get(CACHE_KEY_USER_IDENTITY_ID + loginUserId);
        if (!StringUtil.hasText(loginIdentityId)) {
            UserIdentityVO identity = getIdentity();
            if (identity != null) {
                loginIdentityId = identity.getId();
                RedisClient.set(CACHE_KEY_USER_IDENTITY_ID + loginUserId, loginIdentityId, 60);
            }
        }

        return loginIdentityId;
    }

    public static String getLoginIdentityId() {
        HttpServletRequest request = WsbpWebContextListener.getRequest();
        return getLoginIdentityId(request);
    }



    public static String getIdentityId() {
        UserIdentityVO identity = getIdentity();
        return identity != null ? identity.getId() : "";
    }

    public static void setLoginIdentityId(String loginUserId, String loginIdentityId) {
        RedisClient.set(CACHE_KEY_USER_IDENTITY_ID + loginUserId, loginIdentityId, Cluster.getTimeoutMillisecond());
    }

    public static String getLoginUserId(HttpServletRequest request) {
        String token = request.getHeader("oauth_token");
        String userId = null;
        if (token != null && !token.isEmpty()) {
            userId = (String)RedisClient.get(token);
        }
        return userId;
    }

    public static String getLoginUserId() {
        HttpServletRequest request = WsbpWebContextListener.getRequest();
        return getLoginUserId(request);
    }

    public static String getLoginUserName() {
        UserVO loginUser = getLoginUser();
        return loginUser != null ? loginUser.getUserName() : "";
    }

    public static UserVO getLoginUser() {
        return getContextService().getUserByUserId(getLoginUserId());
    }

    public static String getLoginDepartment(HttpServletRequest request) {
        String identityId = getLoginIdentityId(request);
        return getContextService().getLoginDepartment(identityId);
    }

    public static String getDeptId() {
        HttpServletRequest request = WsbpWebContextListener.getRequest();
        String identityId = getLoginIdentityId(request);
        return getContextService().getLoginDepartment(identityId);
    }

    public static DepartmentVO getDept() {
        return getContextService().getDepartment(getDeptId());
    }

    public static String getDeptName() {
        DepartmentVO department = getDept();
        return department != null ? department.getName() : "";
    }

    public static String getLoginOrganization(HttpServletRequest request) {
        String identityId = getLoginIdentityId(request);
        return getContextService().getLoginOrganization(identityId);
    }

    public static String getOrgId() {
        HttpServletRequest request = WsbpWebContextListener.getRequest();
        String identityId = getLoginIdentityId(request);
        return getContextService().getLoginOrganization(identityId);
    }

    public static OrganizationVO getOrg() {
        DepartmentVO dept = getDept();
        return dept != null ? dept.getOrg() : null;
    }

    public static String getOrgName() {
        OrganizationVO org = getOrg();
        return org != null ? org.getName() : "";
    }

    public static boolean isInAnyRole(HttpServletRequest request, String roles) {
        String identityId = getLoginIdentityId(request);
        return getContextService().checkIsInAnyRole(identityId, roles);
    }

    public static boolean isInRole(HttpServletRequest request, String roles) {
        String identityId = getLoginIdentityId(request);
        return getContextService().checkIsInRole(identityId, roles);
    }

    public static List<RoleVO> getRoleListInRoles(HttpServletRequest request, String roles) {
        String identityId = getLoginIdentityId(request);
        return getContextService().getRoleListInRoles(identityId, roles);
    }

    public static boolean isInAnyCase(HttpServletRequest request, String cases) {
        String identityId = getLoginIdentityId(request);
        return getContextService().checkIsInAnyCase(identityId, cases);
    }

    public static boolean isInCase(HttpServletRequest request, String cases) {
        String identityId = getLoginIdentityId(request);
        boolean checkIsInCase = getContextService().checkIsInCase(identityId, cases);
        return checkIsInCase;
    }

    public static List<RoleVO> getmyUserRoleList(HttpServletRequest request) {
        String identityId = getLoginIdentityId(request);
        return getContextService().getmyUserRoleList(identityId);
    }

    public static RoleVO getmyUserhighestRole(HttpServletRequest request) {
        String identityId = getLoginIdentityId(request);
        return getContextService().getmyUserhighestRole(identityId);
    }

    public static String getmyTenementIdByUserId(HttpServletRequest request) {
        String userId = getLoginUserId(request);
        return getContextService().getLoginTenementId(userId);
    }


    public static void setLoginUser(HttpServletResponse response, TsysUserVO user, String userKey) {
        try {
            String userId = user.getId();
            addCookie(response, USER_KEY, userKey, "/", getSessionTimeout());//單位分鐘
            addCookie(response, Cluster.SESSION_KEY, userKey, "/", getSessionTimeout());//單位分鐘
            RedisClient.set(userKey, userId, Cluster.getTimeoutSecond());//單位秒
            RedisClient.set(userKey+"Username", user.getUserName(), Cluster.getTimeoutSecond());//單位秒
            RedisClient.set(user.getUserName(), user.getPasswd(), Cluster.getTimeoutSecond());//單位秒
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void addCookie(HttpServletResponse response, String name, String value, String path, int maxAge) {
        Cookie cookie = new Cookie(name, value);
        cookie.setPath(path);
        cookie.setMaxAge(maxAge);
        response.addCookie(cookie);
    }

3.登錄接口

登錄邏輯判斷用戶名密碼,通過則將用戶信息返回键袱,同時(shí)將用戶信息存在Redis中燎窘,向前端返回token
代碼如下(示例):

    @RequestMapping(value = "/login", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
    @ResponseBody
    public String login(@RequestBody String jsonStr, HttpServletResponse response) {
        JSONObject object = JSON.parseObject(jsonStr);
        String account = object.getString("account");
        String password = object.getString("password");
        JSONObject result = new JSONObject(3);
        if (StringUtils.isEmpty(account) || StringUtils.isEmpty(password)) {
            result .put("state", false);
            result .put("msg", "賬號(hào)或密碼不能為空");
            return result.toString();
        }
        // 判斷用戶名和密碼
        User userInfo = userService.login(account, password);
        if (userInfo instanceof String) {
            return (String) userInfo;
        } else {
            User user = (User) userInfo;
            String id = user.getId();
            String s = id + System.currentTimeMillis();
            String token = MD5Util.MD5(s);
            Author.setLoginUserId(token, user.getId());
            AuthSessionContext.setLoginUser(response, user, token);
            result .put("state", true);
            result .put("oauth_token", token);
        }
        return result.toString();
    }

4.登錄之后獲取用戶信息

通過擴(kuò)展工具類獲取信息。
代碼如下(示例):

String userId = AuthSessionContext.getLoginUserId();
User userInfo = AuthSessionContext.getLoginUser();

總結(jié)

基于token最直觀的好處就是無狀態(tài)和可擴(kuò)展性蹄咖,token可以設(shè)置過期時(shí)間荠耽,超過時(shí)間之后,用戶將重新登錄比藻。還可以根據(jù)相同的授權(quán)許可使特定的token甚至一組token無效來實(shí)現(xiàn)用戶退出登錄铝量。

創(chuàng)作不易倘屹,關(guān)注、點(diǎn)贊就是對(duì)作者最大的鼓勵(lì)慢叨,歡迎在下方評(píng)論留言
求關(guān)注纽匙,定期分享Java知識(shí),一起學(xué)習(xí)拍谐,共同成長(zhǎng)烛缔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市轩拨,隨后出現(xiàn)的幾起案子践瓷,更是在濱河造成了極大的恐慌,老刑警劉巖亡蓉,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晕翠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡砍濒,警方通過查閱死者的電腦和手機(jī)淋肾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爸邢,“玉大人樊卓,你說我怎么就攤上這事「芎樱” “怎么了碌尔?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)券敌。 經(jīng)常有香客問我唾戚,道長(zhǎng),這世上最難降的妖魔是什么陪白? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮膳灶,結(jié)果婚禮上咱士,老公的妹妹穿的比我還像新娘。我一直安慰自己轧钓,他們只是感情好序厉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著毕箍,像睡著了一般弛房。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上而柑,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天文捶,我揣著相機(jī)與錄音荷逞,去河邊找鬼。 笑死粹排,一個(gè)胖子當(dāng)著我的面吹牛种远,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播顽耳,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼坠敷,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了射富?” 一聲冷哼從身側(cè)響起膝迎,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胰耗,沒想到半個(gè)月后限次,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宪郊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年掂恕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弛槐。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡懊亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乎串,到底是詐尸還是另有隱情店枣,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布叹誉,位于F島的核電站鸯两,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏长豁。R本人自食惡果不足惜钧唐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匠襟。 院中可真熱鬧钝侠,春花似錦、人聲如沸酸舍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)啃勉。三九已至忽舟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叮阅。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工刁品, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帘饶。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓哑诊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親及刻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子镀裤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容