1.什么是JWT
Json web token (JWT), 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標準((RFC 7519).該token被設(shè)計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認證的用戶身份信息寒砖,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認證著蟹,也可被加密歇盼。
2.優(yōu)點
體積小违寿、傳輸快
支持跨域授權(quán),因為跨域無法共享cookie
分布式系統(tǒng)中扫倡,很好地解決了單點登錄問題
3.使用場景
1. 認證庙洼,這是比較常見的使用場景,只要用戶登錄過一次系統(tǒng)镊辕,之后的請求都會包含簽名出來的token油够,通過token也可以用來實現(xiàn)單點登錄。
2. 交換信息征懈,通過使用密鑰對來安全的傳送信息石咬,可以知道發(fā)送者是誰、放置消息被篡改卖哎。
4.項目克隆地址(以下過程代碼簡寫鬼悠,可自行克隆代碼查看實現(xiàn)過程,這個框架是我自己搭建的亏娜,里邊已經(jīng)集成了很多東西焕窝,喜歡的可以研究研究,都是比較常用的技術(shù)维贺,有什么問題也可以評論它掂,看到會回復(fù),賬號密碼 admin? ?admin)
ssh://admin@121.36.107.248:29418/goudan.git
5.集成過程
(1)導入sql表,我把sql文件放在下圖所示位置
sql文件
(2)添加依賴
依賴
(3)主體有5個文件需要添加虐秋,分別是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過濾
? ? ? ? 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? 過濾器(oauth2是一種規(guī)范,springcloud有個springsecurity oauth2是實現(xiàn)靶剑,這里用的jwt)
OAuth2Filer
具體代碼如下
/**
* @program: xiaowu
* @description: oauth2過濾器
* @author: Wu
* @create: 2020-08-29 10:09
**/
public class OAuth2Filterextends AuthenticatingFilter {
@Override
? ? protected AuthenticationTokencreateToken(ServletRequest request, ServletResponse response)throws Exception {
//獲取請求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 {
//獲取請求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;
? ? }
/**
? ? * 獲取請求的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配置(這個里面可以設(shè)置角色缎讼、權(quán)限和認證信息)
OAuth2Realm
具體代碼如下
/**
* @program: xiaowu
* @description: 認證
* @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)(驗證權(quán)限時調(diào)用, 控制role 和 permissins時使用)
*/
? ? @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) {
// 超級管理員-權(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;
? ? }
/**
? ? * 認證(登錄時調(diào)用)
*/
? ? @Override
? ? protected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException {
String accessToken = (String) token.getPrincipal();
? ? ? ? //根據(jù)accessToken,查詢用戶信息
? ? ? ? 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失效阐污,請重新登錄");
? ? ? ? }
//查詢用戶信息
? ? ? ? ManagerInfo managerInfo =managerService.getManagerInfo(managerToken.managerId);
? ? ? ? //賬號鎖定
? ? ? ? // if(managerInfo.getStatus() == 0){
? ? ? ? //? ? throw new LockedAccountException("賬號已被鎖定,請聯(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.測試(這里以登錄接口驗證為例)
登錄接口
實現(xiàn)層代碼
實現(xiàn)層
mapper文件還有這種寫法(頭一次見休涤,以前都是在業(yè)務(wù)代碼里寫的)
xml文件
postman測試
測試
mysql
接口權(quán)限已經(jīng)完成,剩下的就是根據(jù)過期時間的業(yè)務(wù)代碼笛辟,當然這個token加密算法比較簡單功氨,我們可以更換加密算法,這以后再完善
喜歡請關(guān)注 “蛋皮皮” 微信公眾號手幢!更多干貨等你來學習哦捷凄。