1.什么是JWT
Json web token (JWT), 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開(kāi)放標(biāo)準(zhǔn)((RFC 7519).該token被設(shè)計(jì)為緊湊且安全的,特別適用于分布式站點(diǎn)的單點(diǎn)登錄(SSO)場(chǎng)景。JWT的聲明一般被用來(lái)在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶(hù)身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認(rèn)證,也可被加密。
2.優(yōu)點(diǎn)
體積小、傳輸快
支持跨域授權(quán),因?yàn)榭缬驘o(wú)法共享cookie
分布式系統(tǒng)中,很好地解決了單點(diǎn)登錄問(wèn)題
3.使用場(chǎng)景
1. 認(rèn)證,這是比較常見(jiàn)的使用場(chǎng)景,只要用戶(hù)登錄過(guò)一次系統(tǒng),之后的請(qǐng)求都會(huì)包含簽名出來(lái)的token,通過(guò)token也可以用來(lái)實(shí)現(xiàn)單點(diǎn)登錄。
2. 交換信息,通過(guò)使用密鑰對(duì)來(lái)安全的傳送信息,可以知道發(fā)送者是誰(shuí)、放置消息被篡改。
4.項(xiàng)目克隆地址(以下過(guò)程代碼簡(jiǎn)寫(xiě),可自行克隆代碼查看實(shí)現(xiàn)過(guò)程,這個(gè)框架是我自己搭建的,里邊已經(jīng)集成了很多東西,喜歡的可以研究研究,都是比較常用的技術(shù),有什么問(wèn)題也可以評(píng)論,看到會(huì)回復(fù),賬號(hào)密碼 admin? ?admin)
ssh://admin@121.36.107.248:29418/goudan.git
5.集成過(guò)程
(1)導(dǎo)入sql表,我把sql文件放在下圖所示位置

sql文件
(2)添加依賴(lài)

依賴(lài)
(3)主體有5個(gè)文件需要添加,分別是shiroConfig、OAuth2Filer配置、OAuth2Realm、OAuth2Token、TokenGenerator
3.1?shiroConfig

shiroConfig
具體代碼如下
/**
* @program: xiaowu
* @description: Shiro配置
* @author: Wu
* @create: 2020-08-29 09:46
**/
@Configuration
public class ShiroConfig {
@Bean("sessionManager")
public SessionManagersessionManager(){
DefaultWebSessionManager sessionManager =new DefaultWebSessionManager();
? ? ? ? sessionManager.setSessionValidationSchedulerEnabled(true);
? ? ? ? sessionManager.setSessionIdCookieEnabled(true);
? ? ? ? return sessionManager;
? ? }
@Bean("securityManager")
public SecurityManagersecurityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager) {
DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
? ? ? ? securityManager.setRealm(oAuth2Realm);
? ? ? ? securityManager.setSessionManager(sessionManager);
? ? ? ? return securityManager;
? ? }
@Bean("shiroFilter")
public ShiroFilterFactoryBeanshirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter =new ShiroFilterFactoryBean();
? ? ? ? shiroFilter.setSecurityManager(securityManager);
? ? ? ? //oauth過(guò)濾
? ? ? ? Map filters =new HashMap<>();
? ? ? ? filters.put("oauth2", new OAuth2Filter());
? ? ? ? shiroFilter.setFilters(filters);
? ? ? ? Map filterMap =new LinkedHashMap<>();
? ? ? ? filterMap.put("/druid/**", "anon");
? ? ? ? filterMap.put("/app/**", "anon");
? ? ? ? filterMap.put("/login", "anon");
? ? ? ? filterMap.put("/**", "oauth2");
? ? ? ? shiroFilter.setFilterChainDefinitionMap(filterMap);
? ? ? ? return shiroFilter;
? ? }
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessorlifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
? ? }
@Bean
? ? public DefaultAdvisorAutoProxyCreatordefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator =new DefaultAdvisorAutoProxyCreator();
? ? ? ? proxyCreator.setProxyTargetClass(true);
? ? ? ? return proxyCreator;
? ? }
@Bean
? ? public AuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor =new AuthorizationAttributeSourceAdvisor();
? ? ? ? advisor.setSecurityManager(securityManager);
? ? ? ? return advisor;
? ? }
}
3.2? OAuth2Filer? 過(guò)濾器(oauth2是一種規(guī)范,springcloud有個(gè)springsecurity oauth2是實(shí)現(xiàn),這里用的jwt)

OAuth2Filer
具體代碼如下
/**
* @program: xiaowu
* @description: oauth2過(guò)濾器
* @author: Wu
* @create: 2020-08-29 10:09
**/
public class OAuth2Filterextends AuthenticatingFilter {
@Override
? ? protected AuthenticationTokencreateToken(ServletRequest request, ServletResponse response)throws Exception {
//獲取請(qǐng)求token
? ? ? ? String token = getRequestToken((HttpServletRequest) request);
? ? ? ? if(StringUtil.isBlank(token)){
return null;
? ? ? ? }
return new OAuth2Token(token);
? ? }
@Override
? ? protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){
return true;
? ? ? ? }
return false;
? ? }
@Override
? ? protected boolean onAccessDenied(ServletRequest request, ServletResponse response)throws Exception {
//獲取請(qǐng)求token,如果token不存在,直接返回401
? ? ? ? HttpServletRequest httpServletRequest = (HttpServletRequest) request;
? ? ? ? String token = getRequestToken((HttpServletRequest) request);
? ? ? ? if(StringUtil.isBlank(token)){
HttpServletResponse httpResponse = (HttpServletResponse) response;
? ? ? ? ? ? httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
? ? ? ? ? ? httpResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
? ? ? ? ? ? JSONObject json =new JSONObject();
? ? ? ? ? ? json.put("code", "401");
? ? ? ? ? ? json.put("msg", "invalid token");
? ? ? ? ? ? httpResponse.getWriter().print(json);
return false;
? ? ? ? }
return executeLogin(request, response);
? ? }
@Override
? ? protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
? ? ? ? HttpServletRequest httpServletRequest = (HttpServletRequest) request;
? ? ? ? httpResponse.setContentType("application/json;charset=utf-8");
? ? ? ? httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
? ? ? ? httpResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
? ? ? ? try {
//處理登錄失敗的異常
? ? ? ? ? ? Throwable throwable = e.getCause() ==null ? e : e.getCause();
? ? ? ? ? ? JSONObject json =new JSONObject();
? ? ? ? ? ? json.put("code", "401");
? ? ? ? ? ? json.put("msg", throwable.getMessage());
? ? ? ? ? ? httpResponse.getWriter().print(json);
? ? ? ? }catch (IOException e1) {
}
return false;
? ? }
/**
? ? * 獲取請(qǐng)求的token
*/
? ? private StringgetRequestToken(HttpServletRequest httpRequest){
//從header中獲取token
? ? ? ? String token = httpRequest.getHeader("token");
? ? ? ? //如果header中不存在token,則從參數(shù)中獲取token
? ? ? ? if(StringUtil.isBlank(token)){
token = httpRequest.getParameter("token");
? ? ? ? }
return token;
? ? }
}
3.3 OAuth2Realm配置(這個(gè)里面可以設(shè)置角色、權(quán)限和認(rèn)證信息)

OAuth2Realm
具體代碼如下
/**
* @program: xiaowu
* @description: 認(rèn)證
* @author: Wu
* @create: 2020-08-29 10:18
**/
@Component
public class OAuth2Realmextends AuthorizingRealm {
@Autowired
? ? private ManagerServicemanagerService;
? ? @Override
? ? public boolean supports(AuthenticationToken token) {
return tokeninstanceof OAuth2Token;
? ? }
/**
? ? * 授權(quán)(驗(yàn)證權(quán)限時(shí)調(diào)用, 控制role 和 permissins時(shí)使用)
*/
? ? @Override
? ? protected AuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principals) {
ManagerInfo manager = (ManagerInfo)principals.getPrimaryPrincipal();
? ? ? ? Integer managerId = manager.getManagerId();
? ? ? ? SimpleAuthorizationInfo info =new SimpleAuthorizationInfo();
? ? ? ? // 模擬權(quán)限和角色
? ? ? ? Set permsSet =new HashSet<>();
? ? ? ? Set roles =new HashSet<>();
? ? ? ? if (managerId ==1) {
// 超級(jí)管理員-權(quán)限
? ? ? ? ? ? permsSet.add("delete");
? ? ? ? ? ? permsSet.add("update");
? ? ? ? ? ? permsSet.add("view");
? ? ? ? ? ? roles.add("admin");
? ? ? ? }else {
// 普通管理員-權(quán)限
? ? ? ? ? ? permsSet.add("view");
? ? ? ? ? ? roles.add("test");
? ? ? ? }
info.setStringPermissions(permsSet);
? ? ? ? info.setRoles(roles);
? ? ? ? return info;
? ? }
/**
? ? * 認(rèn)證(登錄時(shí)調(diào)用)
*/
? ? @Override
? ? protected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException {
String accessToken = (String) token.getPrincipal();
? ? ? ? //根據(jù)accessToken,查詢(xún)用戶(hù)信息
? ? ? ? ManagerToken managerToken =managerService.queryByToken(accessToken);
? ? ? ? //token失效
? ? ? ? SimpleDateFormat sm =new SimpleDateFormat("yyyyMMddHHmmss");
? ? ? ? Date expireTime;
? ? ? ? boolean flag =true;
? ? ? ? try {
expireTime? ? = sm.parse(managerToken.getExpireTime());
? ? ? ? ? ? flag = managerToken ==null || expireTime.getTime() < System.currentTimeMillis();
? ? ? ? }catch (ParseException e) {
e.printStackTrace();
? ? ? ? }
if(flag){
throw new IncorrectCredentialsException("token失效,請(qǐng)重新登錄");
? ? ? ? }
//查詢(xún)用戶(hù)信息
? ? ? ? ManagerInfo managerInfo =managerService.getManagerInfo(managerToken.managerId);
? ? ? ? //賬號(hào)鎖定
? ? ? ? // if(managerInfo.getStatus() == 0){
? ? ? ? //? ? throw new LockedAccountException("賬號(hào)已被鎖定,請(qǐng)聯(lián)系管理員");
// }
? ? ? ? SimpleAuthenticationInfo info =new SimpleAuthenticationInfo(managerInfo, accessToken, getName());
? ? ? ? return info;
? ? }
}
3.4?OAuth2Token設(shè)置

OAuth2Token
3.5?TokenGenerator, 生成token

TokenGenerator
具體代碼
/**
* @program: xiaowu
* @description: 生成token
* @author: Wu
* @create: 2020-08-29 10:26
**/
public class TokenGenerator {
public static StringgenerateValue() {
return generateValue(UUID.randomUUID().toString());
? ? }
private static final char[]hexCode ="0123456789abcdef".toCharArray();
? ? public static StringtoHexString(byte[] data) {
if (data ==null) {
return null;
? ? ? ? }
StringBuilder r =new StringBuilder(data.length *2);
? ? ? ? for (byte b : data) {
r.append(hexCode[(b >>4) &0xF]);
? ? ? ? ? ? r.append(hexCode[(b &0xF)]);
? ? ? ? }
return r.toString();
? ? }
public static StringgenerateValue(String param) {
try {
MessageDigest algorithm = MessageDigest.getInstance("MD5");
? ? ? ? ? ? algorithm.reset();
? ? ? ? ? ? algorithm.update(param.getBytes());
? ? ? ? ? ? byte[] messageDigest = algorithm.digest();
? ? ? ? ? ? return toHexString(messageDigest);
? ? ? ? }catch (Exception e) {
throw new RuntimeException("生成Token失敗", e);
? ? ? ? }
}
}
4.測(cè)試(這里以登錄接口驗(yàn)證為例)

登錄接口
實(shí)現(xiàn)層代碼

實(shí)現(xiàn)層
mapper文件還有這種寫(xiě)法(頭一次見(jiàn),以前都是在業(yè)務(wù)代碼里寫(xiě)的)

xml文件
postman測(cè)試

測(cè)試

mysql
接口權(quán)限已經(jīng)完成,剩下的就是根據(jù)過(guò)期時(shí)間的業(yè)務(wù)代碼,當(dāng)然這個(gè)token加密算法比較簡(jiǎn)單,我們可以更換加密算法,這以后再完善
喜歡請(qǐng)關(guān)注 “蛋皮皮” 微信公眾號(hào)!更多干貨等你來(lái)學(xué)習(xí)哦。