Este artigo apresenta o método de Spring-Boot combinado com a Shrio para implementar o JWT e compartilha com você, como segue:
Em relação à verificação, existem aproximadamente dois aspectos:
Solução Principal: Use um filtro Shiro personalizado
Construção do projeto:
Este é um projeto da Web de Spring-Boot. Se você não souber sobre a construção do projeto Spring-Boot, pesquise no Google.
Pom.MX apresenta pacotes de jar relacionados
<!-- shiro permission management--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <versão> $ {shiro.version} </versão> </dependency> <!-jwt-> <pendency> <puperid> io.jsonwebtoken </groupid> <stifactId> jjwt </artifactId> <versão 0.9.0 </versão </dependência> Configuração relacionada ao Shrio
Deixe o ponto! ! Personalizado um filtro
filtermap.put ("jwtfilter", new jwtfilter ()); @ConfigurationPublic Classe Shiroconfig {@Bean Public ShirofilterFactoryBean GetShirofilterFactoryBean (SecurityManager SecurityManager) {ShirofilterFactoryBean ShirofilterFactoryBean = New ShirofilterFactoryBean (); ShirofilterFactoryBean.SetSecurityManager (SecurityManager); // Adicione seu próprio filtro e nomeie -o JWTFilter mapa <string, filtro> filtermap = new hashmap <> (); filtermap.put ("jwtfilter", new jwtfilter ()); ShirofilterFactoryBean.setFilters (filterMap); / * * Regras de URL personalizadas * http://shiro.apache.org/web.html#urls- */map <string, string> filterChaindefinitionMap = shirofilterFactoryBean.getFilterChaindefinitionMap (); filterChainndefinitionMap.put ("/**", "jwtfilter"); shirofilterFactoryBean.SetFilterChaindEfinitionMap (FilterChaIndEfinitionMap); Retornar ShirofilterFactoryBean; }/** * O SecurityManager não precisa injetar diretamente o ShirodBrealm, o que pode causar falha na transação * para a solução, consulte HandleContextrefresh * http://www.debugurun.com/mkwebs9ejq.html */@Bean ("SecurityManager") Public StafweBers {DefaultWebSecurityManager gerente = new DefaultWebSecurityManager (); gerente.setRealM (TokenRealM); / * * Feche a sessão que vem com Shiro, consulte a documentação para obter detalhes * http://shiro.apache.org/session-management.html#sessionmanagement-SetSetSapplications%28session%29 */defaultSubjectdao subjectDao = novo defaultsjecjectdao (); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator (); defaultSessionStorageEvaluator.SetSessionStorageEnabled (false); SubjectDao.SetSessionStorageEvaluator (defaultSessionStorageEvaluator); gerente.SetSubjectDao (sujeito); gerente de retorno; } @Bean Public LifeCycleBeanPostProcessor LifeCycleBeanPostProcessor () {return new LifeCycleBeanPostProcessor (); } @Bean (name = "tokenRealm") @dependson ("LifeCycleBeanPostProcessor") TokenRealM TokenRealM () {return New TokenRealm (); } @Bean @Dependson ("LifeCycleBeanPostProcessor") Public DefaultAdvisorAutoProxycreator DefaultAdvisorAutoProxycreator () {defaultAdvisoraUTOPROXYCRATERATAdVISORROUTOPROXYCROTROTROTROTOPROTVISTROTIROTORPROTORTROTOR = NewFaultAxadVis; // force cglib para evitar o proxy duplicado e possíveis erros de procuração // https://zhuanlan.zhihu.com/p/29161098 DefaultAdvisorautoProxycreator.setProxyTargetClass (True); Retornar DefaultAdvisoraToProxyCreator; } @Bean Public AuthorizationAttributesOrCeadVisor GetAuthorizationAttributesOrCeadVisor (SecurityManager SecurityManager) {AuthorizationAttributesourCeadVisor AuthorizationAttributesourCeadvisor = new AuthorizationAttributesourCeadVisor (); AuthorizationAttributesourCeadvisor.SetSecurityManager (SecurityManager); Retornar nova autorizaçãoAttributesourCeadvisor (); }} Personalize o filtro Shrio
Ordem de execução: prehandle -> dofilterinternal -> executelogin -> onloginsuccess
O julgamento principal é se a solicitação de login é dofilterinternal
classe pública JWTFilter estende BasichttPauthenticationFilter { / *** Personalize o método para executar o login* / @Override protegido boolean ExecutELOGIN (Solicitação de servletRequest, resposta de servletResponse; UserNamePasswordToken UsernamePasswordToken = JSON.PARSEOBJECT (httpServletRequest.getInputStream (), UsernamePasswordToken.class); // Envie -o para o Reino para o login. Se o erro estiver errado, ele lançará uma exceção e será pego sujeito = this.getSubject (solicitação, resposta); sujeito.Login (UsernamePasswordToken); retornar this.onlogInsuccess (UsernamePasswordToken, sujeito, solicitação, resposta); // Erro lançar uma exceção}/ *** Primeiro método para executar*/ @override protegido boolean prehandle (solicitação servletRequest, resposta servletResponse) lança a exceção {return super.prendle (solicitação, resposta); } / *** Operação de login Após o login bem -sucedido* Adicione o cabeçalho do JWT* / @Override protegido boolean onLogInsuccess (token autenticationToken, sujeito, sujeito servletRequest, servLetResponse) {httpsVletResponsensonsensensensensenservLeverSonsonsene String jwtToken = jwts.builder () .setId (token.getPrincipal (). Tostring ()) .setexpiration (dateTime.now (). Plusminutes (30) .todate (). httpServletResponse.addHeader (Authorization_Header, JWTToken); retornar true; } /** * The main process of login and verification* Determine whether it is login, or an ordinary request after login*/ @Override public void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpServletResponse httpServletResponse = (httpServletResponse) servletResponse; String servletPath = httpServletRequest.getServletPath (); if (stringUtils.equals (servletPath, "/login")) {// ExecutELOGIN (servletRequest, servletResponse); } else {string autenticationHeader = httpServletRequest.getheader (Authorization_Header); if (stringUtils.isnotEmpty (AuthenticationHeader)) {reivindicações body = jwts.parser () .SetSigningKey (JWTCost.SignatureKey) .ParSeclaimSJWS (AuthenticationHeader) .getBody (); if (body! = null) {// Atualize o token body.setexpiration (dateTime.now (). plusminutes (30) .todate ()); String updateToken = JWTS.Builder (). SetClaims (Body) .compact (); httpServletResponse.addHeader (Authorization_Header, UPDATETOKEN); // Adicionar credenciais do usuário Principais diretores da coleta = new SimplePrincipalCollection (body.getid (), jwtcost.usernamePasswordRealm); // Assenhem informações do usuário shiro webjecject.builder Builder = new websubject.builder (servlet -sequest, servletSponsonsonsion); construtor.Principals (diretores); construtor.authenticated (verdadeiro); construtor.SessionCreationEnabled (false); WebSubject Assunto = Builder.BuildWebSubject (); // coloque no contêiner e ligue para ThreadContext.bind (sujeito); filterchain.dofilter (httpServletRequest, httpServletResponse); }} else {httpServletResponse.setStatus (httpstatus.forbidden.value ()); }}}} Processamento com falha de login
Lidar com exceções Shrio
@RestControllerAdvicePublic Classe GlobalControlleRexceptionHandler {@ExceptionHandler (value = excepcion.class) Public Object AllexceptionHandler (httpServletReQuest solicitação, httpServletResponse Resposta, exceção de exceção) {string message = excepcuseCuse (). GetMessage (); Logutil.error (mensagem); Retornar novo resultadoInfo (excepcion.getClass (). getName (), mensagem); } /*=========== SHIRO Exception Intercept ==========================* / @ExceptionHandler (Valor = IncorrectCredentialSexception.Class) public String IncorrectCrerectentialSception (HTTPSETLETRECESTEMEN) Response.setStatus (httpstatus.forbidden.value ()); retornar "incorretoCredentialSexception"; } @ExceptionHandler (value = UnknownAccountException.class) public String UnknownAccountException (solicitação httpServletRequest, resposta httpSLeTletResponse, exceção de exceção) {Response.SetStatus (httpstatus.forbidden.value ()); retornar "UnknownAccountException"; } @ExceptionHandler (Value = LockedAccountException.Class) public String LockEdAccountException (solicitação HTTPSERVLEQUESTREQUEST, Resposta HttPervletResponse, exceção de exceção) {Response.SetStatus (httpstatus.forbidden.value ()); retornar "LockedAccountException"; } @ExceptionHandler (value = ExcessiveATTEMptSexception.Class) public String ExcessiveatTEMptSexception (solicitação httpServletRequest, resposta httpSertResponse, exceção de exceção) {Response.setStatus (httpstatus. proibidden.value ()); retornar "ExcessiveAtTemptSexception"; } @ExceptionHandler (value = autenticaçãoException.class) public string autenticationException (httpServletRequest Solicy, httpServletResponse Resposta, exceção de exceção) {Response.setStatus (httpstatus.forbidden.value ()); retornar "AuthenticationException"; } @ExceptionHandler (value = UNAuthorizedException.Class) public String UNAuthorizedException (solicitação httpServletRequest, resposta httpSertletResponse, exceção de exceção) {Response.SetStatus (httpstatus. proibidden.value ()); retornar "UnauthorizedException"; }}Manuseando exceções JWT
Esta é uma armadilha, porque é uma exceção que ocorre no filtro, e o @ExceptionHandler não pode interceptá -lo.
/*** intercepto de erro de inicialização da mola*/ @RestControllerPublic Classe GlobalexceptionHandler implementa ErrorController {@Override public String getErRorPath () {return "/Error"; } @ReQuestMapping (value = "/error") Erro do objeto público (solicitação httpServletRequest, httpServletResponse resposta) lança exceção {// lidação de erros de exceção lógica = (exceção) request.getAttribute ("javax.servlet.error.exception"); Lançável causa = excepção.getcause (); if (porque a instância de expirejwtexception) {Response.SetStatus (httpstatus.gateway_timeout.value ()); Retorne o novo resultado ("expirejwtexception", caus.getMessage ()); } if (causa instância de malformedjwtexception) {Response.SetStatus (httpstatus.forbidden.value ()); Retorne o novo ResultInfo ("MalformedJwtexception", Caus.getMessage ()); } Retorne novo resultadoInfo (caus.getCausa (). getMessage (), caus.getMessage ()); }}Em relação às informações de autorização, como permissões, você pode colocá -las diretamente no Redis em cache. Eu acho que é bom também.
Código-fonte Presents: Githup-shiro Branch: Lembrete quente: O código de teste pode ser confuso na vida cotidiana.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.