日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

json web token没有哪个成分_【分享项目】给你看看我们公司的登录认证是怎么做的?!(SpringBoot+Shiro+Token+Redis)...

發(fā)布時(shí)間:2023/12/18 javascript 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 json web token没有哪个成分_【分享项目】给你看看我们公司的登录认证是怎么做的?!(SpringBoot+Shiro+Token+Redis)... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

背景交代

以前項(xiàng)目中權(quán)限認(rèn)證沒(méi)有使用安全框架,都是在自定義filter中判斷是否登錄以及用戶(hù)是否有操作權(quán)限的。最近開(kāi)了新項(xiàng)目,搭架子時(shí),想到使用安全框架來(lái)解決認(rèn)證問(wèn)題,spring security太過(guò)龐大,我們的項(xiàng)目不大,所以決定采用Shiro。

什么是Shiro

Apache Shiro 是一個(gè)強(qiáng)大靈活的開(kāi)源安全框架,可以完全處理身份驗(yàn)證、授權(quán)、加密和會(huì)話管理。

Realm是Shiro的核心組建,也一樣是兩步走,認(rèn)證和授權(quán),在Realm中的表現(xiàn)為以下兩個(gè)方法。

  • 認(rèn)證:doGetAuthenticationInfo,核心作用判斷登錄信息是否正確
  • 授權(quán):doGetAuthorizationInfo,核心作用是獲取用戶(hù)的權(quán)限字符串,用于后續(xù)的判斷

Shiro過(guò)濾器

當(dāng) Shiro 被運(yùn)用到 web 項(xiàng)目時(shí),Shiro 會(huì)自動(dòng)創(chuàng)建一些默認(rèn)的過(guò)濾器對(duì)客戶(hù)端請(qǐng)求進(jìn)行過(guò)濾。以下是 Shiro 提供的部分過(guò)濾器:

過(guò)濾器描述
anon表示可以匿名使用
authc表示需要認(rèn)證(登錄)才能使用
authcBasic表示httpBasic認(rèn)證
perms當(dāng)有多個(gè)參數(shù)時(shí)必須每個(gè)參數(shù)都通過(guò)才通過(guò) perms[“user:add:”]
portport[8081] 跳轉(zhuǎn)到schemal://serverName:8081?queryString
rest權(quán)限
roles角色
ssl表示安全的url請(qǐng)求
user表示必須存在用戶(hù),當(dāng)?shù)侨氩僮鲿r(shí)不做檢查

為什么選擇shiro

  • 簡(jiǎn)單性,Shiro 在使用上較 Spring Security 更簡(jiǎn)單,更容易理解。
  • 靈活性,Shiro 可運(yùn)行在 Web、EJB、IoC、Google App Engine 等任何應(yīng)用環(huán)境,卻不依賴(lài)這些環(huán)境。而 Spring Security 只能與 Spring 一起集成使用。
  • 可插拔,Shiro 干凈的 API 和設(shè)計(jì)模式使它可以方便地與許多的其它框架和應(yīng)用進(jìn)行集成。Shiro 可以與諸如 Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin 這類(lèi)第三方框架無(wú)縫集成。Spring Security 在這方面就顯得有些捉襟見(jiàn)肘。

spring boot整合shiro

添加maven依賴(lài) 在項(xiàng)目中引入shiro非常簡(jiǎn)單,我們只需要引入 shiro-spring 就可以了


<dependency>
??<groupId>org.apache.shirogroupId>
??<artifactId>shiro-springartifactId>
??<version>1.4.0version>
dependency>

shiro自定義認(rèn)證token

AuthenticationToken 用于收集用戶(hù)提交的身份(如用戶(hù)名)及憑據(jù)(如密碼)。Shiro會(huì)調(diào)用CredentialsMatcher對(duì)象的doCredentialsMatch方法對(duì)AuthenticationInfo對(duì)象和AuthenticationToken進(jìn)行匹配。匹配成功則表示主體(Subject)認(rèn)證成功,否則表示認(rèn)證失敗。

Shiro 僅提供了一個(gè)可以直接使用的 UsernamePasswordToken,用于實(shí)現(xiàn)基于用戶(hù)名/密碼主體(Subject)身份認(rèn)證。UsernamePasswordToken實(shí)現(xiàn)了 RememberMeAuthenticationToken 和 HostAuthenticationToken,可以實(shí)現(xiàn)“記住我”及“主機(jī)驗(yàn)證”的支持。

我們的業(yè)務(wù)邏輯是每次調(diào)用接口,不使用session存儲(chǔ)登錄狀態(tài),使用在head里面存token的方式,所以不使用session,并不需要用戶(hù)密碼認(rèn)證。

自定義token如下:

/**
?*?Created?by?Youdmeng?on?2020/6/24?0024.
?*/
public?class?YtoooToken?implements?AuthenticationToken?{
????private?String?token;
????public?YtoooToken(String?token)?{
????????this.token?=?token;
????}
????@Override
????public?Object?getPrincipal()?{
????????return?token;
????}
????@Override
????public?Object?getCredentials()?{
????????return?token;
????}
}

shiro自定義Realm

Realm是shiro的核心組件,主要處理兩大功能:

  • 認(rèn)證 我們接收f(shuō)ilter傳過(guò)來(lái)的token,并認(rèn)證login操作的token
  • 授權(quán) 獲取到登錄用戶(hù)信息,并取得用戶(hù)的權(quán)限存入roles,以便后期對(duì)接口進(jìn)行操作權(quán)限驗(yàn)證
@Slf4j
public?class?UserRealm?extends?AuthorizingRealm?{
????@Autowired
????private?JedisClusterClient?jedis;
????/**
?????*?大坑!,必須重寫(xiě)此方法,不然Shiro會(huì)報(bào)錯(cuò)
?????*/
????@Override
????public?boolean?supports(AuthenticationToken?token)?{
????????return?token?instanceof?YtoooToken;
????}
?????/**
?????*?授權(quán)
?????*
?????*?@param?principals
?????*?@return
?????*/
????@Override
????protected?AuthorizationInfo?doGetAuthorizationInfo(PrincipalCollection?principals)?{
????????log.info("Shiro權(quán)限配置");
????????String?token?=?principals.toString();

????????UserDetailVO?userDetailVO?=?JSON.parseObject(jedis.get(token),?UserDetailVO.class);

????????Set?roles?=?new?HashSet<>();
????????roles.add(userDetailVO.getAuthType()?+?"");
????????SimpleAuthorizationInfo?info?=?new?SimpleAuthorizationInfo();
????????info.setRoles(roles);return?info;
????}/**
?????*?認(rèn)證
?????*
?????*?@param?token
?????*?@return
?????*?@throws?AuthenticationException
?????*/@Overrideprotected?AuthenticationInfo?doGetAuthenticationInfo(AuthenticationToken?token)?throws?AuthenticationException?{
????????log.info("Shiror認(rèn)證");
????????YtoooToken?usToken?=?(YtoooToken)?token;//獲取用戶(hù)的輸入的賬號(hào).
????????String?sid?=?(String)?usToken.getCredentials();if?(StringUtils.isBlank(sid))?{return?null;
????????}
????????log.info("sid:?"?+?sid);return?new?SimpleAccount(sid,?sid,?"userRealm");
????}
}

shiro自定義攔截器

自定義shiro攔截器來(lái)控制指定請(qǐng)求的訪問(wèn)權(quán)限,并登錄shiro以便認(rèn)證

我們自定義shiro攔截器主要使用其中的兩個(gè)方法:

  • isAccessAllowed() 判斷是否可以登錄到系統(tǒng)
  • onAccessDenied() 當(dāng)isAccessAllowed()返回false時(shí),登錄被拒絕,進(jìn)入此接口進(jìn)行異常處理
@Slf4j
public?class?TokenFilter?extends?FormAuthenticationFilter?{
????private?String?errorCode;
????private?String?errorMsg;
????private?static?JedisClusterClient?jedis?=?JedisClusterClient.getInstance();
????/**
?????*?如果在這里返回了false,請(qǐng)求onAccessDenied()
?????*/
????@Override
????protected?boolean?isAccessAllowed(ServletRequest?request,?ServletResponse?response,?Object?mappedValue)?{

????????HttpServletRequest?httpServletRequest?=?(HttpServletRequest)?request;
????????String?sid?=?httpServletRequest.getHeader("sid");
????????if?(StringUtils.isBlank(sid))?{
????????????this.errorCode?=?ResponseEnum.TOKEN_UNAVAILABLE.getCode();
????????????this.errorMsg?=?ResponseEnum.TOKEN_UNAVAILABLE.getMessage();
????????????return?false;
????????}
????????log.info("sid:?"?+?sid);
????????UserDetailVO?userInfo?=?null;
????????try?{
????????????userInfo?=?JSON.parseObject(jedis.get(sid),?UserDetailVO.class);
????????}?catch?(Exception?e)?{
????????????this.errorCode?=?ResponseEnum.TOKEN_EXPIRE.getCode();
????????????this.errorMsg?=?ResponseEnum.TOKEN_EXPIRE.getMessage();
????????????return?false;
????????}
????????if?(userInfo?==?null)?{
????????????this.errorCode?=?ResponseEnum.TOKEN_EXPIRE.getCode();
????????????this.errorMsg?=?ResponseEnum.TOKEN_EXPIRE.getMessage();
????????????return?false;
????????}
????????//刷新超時(shí)時(shí)間
????????jedis.expire(sid,?30?*?60);?//30分鐘過(guò)期
????????YtoooToken?token?=?new?YtoooToken(sid);
????????//?提交給realm進(jìn)行登入,如果錯(cuò)誤他會(huì)拋出異常并被捕獲
????????getSubject(request,?response).login(token);
????????//?如果沒(méi)有拋出異常則代表登入成功,返回true
????????return?true;
????}
????@Override
????protected?boolean?onAccessDenied(ServletRequest?request,?ServletResponse?response)?{
????????ResponseMessage?result?=?Result.error(this.errorCode,this.errorMsg);
????????String?reponseJson?=?(new?Gson()).toJson(result);
????????response.setContentType("application/json;?charset=utf-8");
????????response.setCharacterEncoding("utf-8");
????????ServletOutputStream?outputStream?=?null;
????????try?{
????????????outputStream?=?response.getOutputStream();
????????????outputStream.write(reponseJson.getBytes());
????????}?catch?(IOException?e)?{
????????????log.error("權(quán)限校驗(yàn)異常",e);
????????}?finally?{
????????????if?(outputStream?!=?null){
????????????????try?{
????????????????????outputStream.flush();
????????????????????outputStream.close();
????????????????}?catch?(IOException?e)?{
????????????????????log.error("權(quán)限校驗(yàn),關(guān)閉連接異常",e);
????????????????}
????????????}
????????}
????????return?false;
????}
}

配置ShiroConfig

springboot中,組件通過(guò)@Bean的方式交由spring統(tǒng)一管理,在這里需要配置 securityManager,shiroFilter,AuthorizationAttributeSourceAdvisor

注入realm

@Bean
public?UserRealm?userRealm()?{
????UserRealm?userRealm?=?new?UserRealm();
????return?userRealm;
}

注入 securityManager

@Bean("securityManager")
public?DefaultWebSecurityManager?getManager(UserRealm?realm)?{
????DefaultWebSecurityManager?manager?=?new?DefaultWebSecurityManager();
????//?使用自己的realm
????manager.setRealm(realm);
????/*
??????*?關(guān)閉shiro自帶的session,詳情見(jiàn)文檔
??????*?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);
????manager.setSubjectDAO(subjectDAO);

????return?manager;
}

注入 shiroFilter

此處將自定義過(guò)濾器添加到shiro中,并配置具體哪些路徑,執(zhí)行shiro的那些過(guò)濾規(guī)則

@Bean("shiroFilter")
public?ShiroFilterFactoryBean?factory(DefaultWebSecurityManager?securityManager)?{
????ShiroFilterFactoryBean?factoryBean?=?new?ShiroFilterFactoryBean();

????//?添加自己的過(guò)濾器并且取名為token
????Map?filterMap?=?new?HashMap<>();
????filterMap.put("token",?new?TokenFilter());
????factoryBean.setFilters(filterMap);
????factoryBean.setSecurityManager(securityManager);/*
??????*?自定義url規(guī)則
??????*?http://shiro.apache.org/web.html#urls-
??????*/
????Map?filterRuleMap?=?new?HashMap<>();//swagger
????filterRuleMap.put("/swagger-ui.html",?"anon");
????filterRuleMap.put("/**/*.js",?"anon");
????filterRuleMap.put("/**/*.png",?"anon");
????filterRuleMap.put("/**/*.ico",?"anon");
????filterRuleMap.put("/**/*.css",?"anon");
????filterRuleMap.put("/**/ui/**",?"anon");
????filterRuleMap.put("/**/swagger-resources/**",?"anon");
????filterRuleMap.put("/**/api-docs/**",?"anon");//swagger//登錄
????filterRuleMap.put("/login/login",?"anon");
????filterRuleMap.put("/login/verifyCode",?"anon");//?所有請(qǐng)求通過(guò)我們自己的JWT?Filter
????filterRuleMap.put("/**",?"token");
????factoryBean.setFilterChainDefinitionMap(filterRuleMap);return?factoryBean;
}

配置DefaultAdvisorAutoProxyCreator

解決 在@Controller注解的類(lèi)的方法中加入@RequiresRole等shiro注解,會(huì)導(dǎo)致該方法無(wú)法映射請(qǐng)求,導(dǎo)致返回404。

@Bean
public?static?DefaultAdvisorAutoProxyCreator?getDefaultAdvisorAutoProxyCreator(){
????DefaultAdvisorAutoProxyCreator?defaultAdvisorAutoProxyCreator=new?DefaultAdvisorAutoProxyCreator();
????/**
??????* setUsePrefix(false)用于解決一個(gè)奇怪的bug。在引入spring aop的情況下。
??????*?在@Controller注解的類(lèi)的方法中加入@RequiresRole等shiro注解,會(huì)導(dǎo)致該方法無(wú)法映射請(qǐng)求,導(dǎo)致返回404。
??????*?加入這項(xiàng)配置能解決這個(gè)bug
??????*/
????defaultAdvisorAutoProxyCreator.setUsePrefix(true);
????return?defaultAdvisorAutoProxyCreator;
}

配置 AuthorizationAttributeSourceAdvisor 使doGetAuthorizationInfo()Shiro權(quán)限配置生效

@Bean
public?AuthorizationAttributeSourceAdvisor?authorizationAttributeSourceAdvisor(DefaultWebSecurityManager?securityManager)?{
????AuthorizationAttributeSourceAdvisor?authorizationAttributeSourceAdvisor?=?new?AuthorizationAttributeSourceAdvisor();
????authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
????return?authorizationAttributeSourceAdvisor;
}?

在接口中控制權(quán)限

使用RequiresRoles注解來(lái)配置該接口需要的權(quán)限

當(dāng)配置logical = Logical.OR時(shí),登錄配置的權(quán)限在1,2,3中任意一個(gè),既可以成功訪問(wèn)接口

@ApiOperation("任務(wù)調(diào)度")
@PostMapping("/dispatch")
@RequiresRoles(value?=?{?"1",?"2",?"3"?},?logical?=?Logical.OR)
public?ResponseMessage?dispatch(@RequestBody?@Valid?DispatchVO?dispatchVO)?{

????log.info("任務(wù)調(diào)度開(kāi)始?入?yún)?"?+?JSON.toJSONString(dispatchVO));
????try?{
????????service.dispatch(dispatchVO);
????????return?Result.success(ResponseEnum.SUCCESS.getCode(),?ResponseEnum.SUCCESS.getMessage());
????}?catch?(RuntimeException?e)?{
????????log.error("任務(wù)調(diào)度失敗",?e);
????????return?Result.error(ResponseEnum.ERROR.getCode(),?e.getMessage());
????}?catch?(Exception?e)?{
????????log.error("任務(wù)調(diào)度失敗",?e);
????????return?Result.error(ResponseEnum.ERROR.getCode(),?ResponseEnum.ERROR.getMessage());
????}
}

統(tǒng)一的異常處理

配置全局異常處理

@ControllerAdvice
@Order(value=1)
public?class?ShiroExceptionAdvice?{

????private?static?final?Logger?logger?=?LoggerFactory.getLogger(ShiroExceptionAdvice.class);
????@ResponseStatus(HttpStatus.UNAUTHORIZED)
????@ExceptionHandler({AuthenticationException.class,?UnknownAccountException.class,
????????????UnauthenticatedException.class,?IncorrectCredentialsException.class})
????@ResponseBody
????public?ResponseMessage?unauthorized(Exception?exception)?{
????????logger.warn(exception.getMessage(),?exception);
????????logger.info("catch?UnknownAccountException");
????????return?Result.error(ResponseEnum.NOT_AUTHORIZED.getCode(),?ResponseEnum.NOT_AUTHORIZED.getMessage());
????}

????@ResponseStatus(HttpStatus.UNAUTHORIZED)
????@ExceptionHandler(UnauthorizedException.class)
????@ResponseBody
????public?ResponseMessage?unauthorized1(UnauthorizedException?exception)?{
????????logger.warn(exception.getMessage(),?exception);
????????return?Result.error(ResponseEnum.NOT_AUTHORIZED.getCode(),?ResponseEnum.NOT_AUTHORIZED.getMessage());
????}
}

配置redis

@Bean
????@DependsOn("ConfigUtil")
????public?JedisClusterClient?getClient()?{

????????ml.ytooo.redis.RedisProperties.expireSeconds?=?redisProperties.getExpireSeconds();
????????ml.ytooo.redis.RedisProperties.clusterNodes?=?redisProperties.getClusterNodes();
????????ml.ytooo.redis.RedisProperties.connectionTimeout?=?redisProperties.getConnectionTimeout();
????????ml.ytooo.redis.RedisProperties.soTimeout?=?redisProperties.getSoTimeout();
????????ml.ytooo.redis.RedisProperties.maxAttempts?=?redisProperties.getMaxAttempts();

????????if?(StringUtils.isNotBlank(redisProperties.password))?{
????????????ml.ytooo.redis.RedisProperties.password?=?redisProperties.password;
????????}else?{
????????????ml.ytooo.redis.RedisProperties.password?=?null;
????????}

????????return?JedisClusterClient.getInstance();
????}
@Data
@Component
@ConfigurationProperties(prefix?=?"redis.cache")
public?class?RedisProperties?{

????private?int?expireSeconds;
????private?String?clusterNodes;
????private?int??connectionTimeout;
????private?String?password;
????private?int?soTimeout;
????private?int?maxAttempts;
}

依賴(lài)工具集:

<dependency>
??<groupId>ml.ytooogroupId>
??<artifactId>ytooo-utilartifactId>
??<version>3.7.0version>
dependency>

關(guān)注我!Java從此不迷路!


作者:我是大月餅

鏈接:

https://blog.csdn.net/Youdmeng/article/details/107179579

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的json web token没有哪个成分_【分享项目】给你看看我们公司的登录认证是怎么做的?!(SpringBoot+Shiro+Token+Redis)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。