springboot2+shiro+jwt整合(一)登入認證
當我們要把伺服器做成無狀態時(即伺服器端不會儲存session),這裡我們就可以用到JWT。
為什麼使用JWT?
1.簡潔(Compact): 可以通過URL,POST引數或者在HTTP header傳送,因為資料量小,傳輸速度也很快。
2.自包含(Self-contained):負載中包含了所有使用者所需要的資訊,避免了多次查詢資料庫。
3.安全(security): 與簡單的JSON相比,XML和XML數字簽名會引入複雜的安全漏洞。
認證原理
1.使用者登陸之後,使用密碼對賬號進行簽名生成並返回token並設定過期時間;
2.將token儲存到本地,並且每次傳送請求時都在header上攜帶token。
3.shiro過濾器攔截到請求並獲取header中的token,並提交到自定義realm的doGetAuthenticationInfo方法。
4.通過jwt解碼獲取token中的使用者名稱,從資料庫中查詢到密碼之後根據密碼生成jwt效驗器並對token進行驗證。
OK,介紹完後上程式碼。
JWT
引入pom
<!--JWT--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
首先我們需要自定義一個物件用來包裝token。
JwtToken
package com.style.orange.shiro; import org.apache.shiro.authc.AuthenticationToken; /** * @author Mr.Li * @create 2018-07-12 15:19 * @desc **/ public class JwtToken implements AuthenticationToken { private String token; public JwtToken(String token) { this.token = token; } @Override public Object getPrincipal() { return token; } @Override public Object getCredentials() { return token; } }
再寫一個工具類用來進行簽名和效驗Token
JwtToken
package com.style.orange.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;
/**
* @author Mr.Li
* @create 2018-07-12 14:23
* @desc JWT工具類
**/
public class JwtUtil {
private static final long EXPIRE_TIME = 5 * 60 * 1000;
/**
* 校驗token是否正確
*
* @param token 金鑰
* @param secret 使用者的密碼
* @return 是否正確
*/
public static boolean verify(String token, String username, String secret) {
try {
//根據密碼生成JWT效驗器
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
//效驗TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 獲得token中的資訊無需secret解密也能獲得
*
* @return token中包含的使用者名稱
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成簽名,5min後過期
*
* @param username 使用者名稱
* @param secret 使用者的密碼
* @return 加密的token
*/
public static String sign(String username, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附帶username資訊
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
}
}
前面認證原理說到我們要使用shiro來攔截token,那就需要我們自己寫一個jwt過濾器來作為shiro的過濾器。
JwtFilter
import com.style.orange.shiro.JwtToken;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Mr.Li
* @create 2018-07-12 15:56
* @desc
**/
public class JwtFilter extends BasicHttpAuthenticationFilter {
/**
* 執行登入認證
*
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
executeLogin(request, response);
return true;
} catch (Exception e) {
return false;
}
}
/**
*
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("Authorization");
JwtToken jwtToken = new JwtToken(token);
// 提交給realm進行登入,如果錯誤他會丟擲異常並被捕獲
getSubject(request, response).login(jwtToken);
// 如果沒有丟擲異常則代表登入成功,返回true
return true;
}
/**
* 對跨域提供支援
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域時會首先發送一個option請求,這裡我們給option請求直接返回正常狀態
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
}
Shiro
引入pom
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
準備自定義Realm
MyRealm
package com.style.orange.shiro;
import com.style.orange.model.SysUser;
import com.style.orange.service.SysUserService;
import com.style.orange.utils.JwtUtil;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author Mr.Li
* @create 2018-07-12 15:23
* @desc
**/
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private SysUserService sysUserService;
/**
* 必須重寫此方法,不然Shiro會報錯
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
/**
* 只有當需要檢測使用者許可權的時候才會呼叫此方法,例如checkRole,checkPermission之類的
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = JwtUtil.getUsername(principals.toString());
SysUser user = sysUserService.findByUserName(username);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
return simpleAuthorizationInfo;
}
/**
* 預設使用此方法進行使用者名稱正確與否驗證,錯誤丟擲異常即可。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
// 解密獲得username,用於和資料庫進行對比
String username = JwtUtil.getUsername(token);
if (username == null) {
throw new AuthenticationException("token無效");
}
SysUser userBean = sysUserService.findByUserName(username);
if (userBean == null) {
throw new AuthenticationException("使用者不存在!");
}
if (!JwtUtil.verify(token, username, userBean.getPassword())) {
throw new AuthenticationException("使用者名稱或密碼錯誤");
}
return new SimpleAuthenticationInfo(token, token, "my_realm");
}
}
ShiroConfig
package com.style.orange.shiro;
import com.style.orange.filter.JwtFilter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author: PENG
* @date: 2018/2/7
* @description: shiro 配置類
*/
@Configuration
public class ShiroConfig {
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//攔截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不會被攔截的連結 順序判斷
filterChainDefinitionMap.put("/login/**", "anon");
filterChainDefinitionMap.put("/**.js", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/swagger**/**", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/v2/**", "anon");
// 新增自己的過濾器並且取名為jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
filterMap.put("jwt", new JwtFilter());
shiroFilterFactoryBean.setFilters(filterMap);
//<!-- 過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊
filterChainDefinitionMap.put("/**", "jwt");
//未授權介面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean("securityManager")
public SecurityManager securityManager(MyRealm myRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
/*
* 關閉shiro自帶的session,詳情見文件
* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
*/
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
}
OK,接下來我們進行測試,我這裡集成了swagger2,測試起來比較方便。
如圖紅色標註就是返回的token,之後我把token放到請求的header中訪問就可以了通過了。
相關推薦
springboot2+shiro+jwt整合(一)登入認證
當我們要把伺服器做成無狀態時(即伺服器端不會儲存session),這裡我們就可以用到JWT。 為什麼使用JWT? 1.簡潔(Compact): 可以通過URL,POST引數或者在HTTP header傳送,因為資料量小,傳輸速度也很快。 2.自包含(Self-conta
springboot2+shiro+jwt整合(二)細粒度許可權控制+使用redis作為快取
簡單來說,當專案啟動起來後,我們的後臺介面的許可權控制就應該起作用了,那麼如何使用shiro來實現呢?我這裡使用的是 如何使用註解來配置細粒度許可權。 首先,shiro預設不支援使用註解方式,需要在ShiroConfig中新增以下程式碼 /** * 下面
深入淺出學Shiro(一)--登入認證
ApacheShiro是一個強大易用的Java安全框架,提供了認證、授權、加密和會話管理等功能: Shiro為解決下列問題,提供了保護應用的API: 認證 - 使用者身份識別,常被稱為使用者“登入”; 授權 - 訪問控制; 密碼加密 - 保護或隱藏資料防止被
shiro spring整合(一)
shiro介紹 (1)簡介 Apache Shiro是Java的一個安全框架。目前,使用Apache Shiro的人越來越多,因為它相當簡單,對比Spring Security,可能沒有Spring Security做的功能強大,但是在實際工作時可能並不需要
shiro許可權控制(一):shiro介紹以及整合SSM框架
shiro安全框架是目前為止作為登入註冊最常用的框架,因為它十分的強大簡單,提供了認證、授權、加密和會話管理等功能 。 shiro能做什麼? 認證:驗證使用者的身份 授權:對使用者執行訪問控制:判斷使用者是否被允許做某事 會話管理:在任
Apache Shiro 使用手冊(一)Shiro架構介紹
springmvc+mybatis dubbo+zookeeper restful redis分布式緩存 shiro kafka 一、什麽是Shiro Apache Shiro是一個強大易用的Java安全框架,提供了認證、授權、加密和會話管理等功能: 認證 - 用戶身份識別,常被稱為用戶“
spring boot2 整合(一)Mybatis (特別完整!)
one OS solver mapper ant gin enabled selectall pom 大概介紹下流程: 借助idea實現mybatis逆向工程 用xml配置實現整合 用cmd命令行實現mybatis逆向工程 用mapping.xml配置實現數據交互
SSM整合(一):創建項目
SSM這裏應用的工具是Eclipse+Maven,jdk版本1.8.x 這裏直接新建maven項目就好了,webapp1.1 主要貼一下pom文件,這個文件其實是循序漸進的。http://mvnrepository.com/這是個神奇的網站! <project xmlns="http://maven.a
python實戰演練(一)登入介面程式
一.實現功能: 1. 使用者輸入帳號密碼進行登陸2. 使用者資訊儲存在檔案內3. 使用者密碼輸入錯誤三次後鎖定使用者 二.流程圖 三.程式碼 #-*- Coding:utf-8 -*- # Author: kking # 建立了user_info是使用者檔案,user_lo
Shiro學習筆記(一)--- 認證與授權
一、簡介 Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。使用Shiro的易於理解的API,您可以快速、輕鬆地獲得任何應用程式,從最小的移動應用程式到最大的網路和企業應用程式。 主要功能 三個核心元件:Subject, Security
Shiro的學習(一)——Shiro介紹
一、介紹 shiro是apache的一個開源框架,是一個許可權管理的框架,實現 使用者認證(登入)、使用者授權。 它將軟體系統的安全認證相關的功能抽取出來,實現使用者身份認證,許可權授權、加密、會話管理等功能,組成了一個通用的安全認證框架。 與其一樣的還有SpringSecurit
getFieldDecorator用法(一)——登入表單
之前使用antd的ui表單,卻沒發現這麼好用的用法,推薦給大家 import React from "react"; import { Card, Form, Input, Button, message, Icon, Checkbox } from "antd"; con
機房收費系統(一)------登入&修改
前言 進行機房也有一段時間了,一直處於走迷宮的狀態不知從何入手。從生活中出發,我們在使用某個系統時,一般情況下最先進入該系統的登入頁面。現在輪到自己敲系統了,當然也是從登入開始啦。 正文 機房收費系統登入窗體的思路,和有原始碼的學生系統的思路是大同
從Elasticsearch詳解Ambari與第三方軟體的整合(一)
一. 簡單介紹 1. 軟體介紹 1)Ambari(HDP) 玩過大資料的人都知道,除了原生的apache hadoop,有兩大hadoop廠商(現在已經合併了。。喜聞樂見。。。):Hortonworks 和 Cloudera。Cloudera的hadoop產品相對來講成
spring-boot2-整合(一)Mybatis-(特別完整!)
整合Mybatis分為兩種模式,一種是xml配置,一種是註解。(類似JPA) 我在這裡重點放在xml配置上,因為如果想用註解的話,建議直接用jpa代替,因為Jpa有更成熟的CRUD介面更方便開發。我在後文中也會把註解方式說清楚。 大概介紹下流程:
Shiro系列專題(一)-什麼是RBAC模型?
角色訪問控制(RBAC)引入了Role的概念,目的是為了隔離User(即動作主體,Subject)與Privilege(許可權,表示對Resource的一個操作,即Operation+Resource)。 Role作為一個使用者(User)與許可權(Privil
SpringBoot2.0填坑(一):使用CROS解決跨域並解決swagger 訪問不了問題
簡介 公司後臺是採用SpringBoot2.0 搭建的微服務架構,前端框架用的是vue 使用前後端分離的開發方式,在開發聯調的時候需要進行跨域訪問,那麼使用CROS解決了跨域問題,但是swagger 卻用不了 具體解決方案請繼續往下看… CROS跨域原理 跨域資源共享(CORS)
騰訊Tinker 熱修復 Andriod studio 3.0 配置和整合(一)
本文說明 面試的時候經常問我有沒有用過熱修復?用誰的?能說下原理嗎?當時我回答得不好,畢竟以前的專案都沒有用,又不敢裝逼,mmp,但是基本流程還是知道的,所以我們來初探下Tinker 這個熱修復,如果我是Andriod studio 2.3的話,我還不怎麼想寫這個文章,畢竟太多了,沒有
Eclipse與GitHub的整合(一)——本地Git倉庫中的程式碼push至GitHub
團隊合作開發一個專案的時候,使用Git版本控制,將程式碼託管到GitHub上對多人合作是非常方便的。下面介紹一下Eclipse與GitHub的整合——本地Git倉庫程式碼push到GitHub上。 前提條件: 1. 本地已安裝Git 2. 有GitHub賬號 3. 開發
apache shiro叢集實現(一) session共享
Apache Shiro的基本配置和構成這裡就不詳細說明了,其官網有說明文件,這裡僅僅說明叢集的解決方案,詳細配置:shiro web config Apache Shiro叢集要解決2個問題,一個是session的共享問題,一個是授權資訊的cache共享問