Visão geral
Como todos sabemos, usando o JWT para verificação de permissão. Comparado com a sessão, a vantagem da sessão é que a sessão requer uma grande quantidade de memória do servidor e, quando vários servidores são usados, ela envolverá problemas de sessão compartilhada, o que é mais problemático ao acessar terminais móveis, como telefones celulares.
O JWT não precisa ser armazenado no servidor e não ocupa recursos do servidor (ou seja, sem estado). Depois que o usuário faz login, ele se anexa ao token ao acessar a solicitação que requer permissão (geralmente definida no cabeçalho da solicitação HTTP). O JWT não tem o problema de compartilhar vários servidores, nem possui problemas de acesso móvel em telefones celulares. Se para melhorar a segurança, o token pode estar vinculado ao endereço IP do usuário
Processo front-end
Usuário conectado através do Ajax para obter um token
Depois disso, ao acessar a permissão, anexe um token ao acesso.
<! Doctype html> <html lang = "en"> <head> <meta charset = "utf-8"> <title> title </title> <script src = "http:/paps.bdimg.com/libs/jquery/2.1.4/jquery.mins"> </slitt) ""; function login () {$ .post ("http: // localhost: 8080/auth/login", {nome de usuário: $ ("#nome de usuário"). val (), senha: $ ("#senha"). "Get", URL: "http: // localhost: 8080/userpage", beforesend: function (request) {request.setRequestHeader ("autorização", cabeçalho); } </script> </ad Head> <body> <dieldset> <legend> Por favor, faça o login </legend> <belt> nome de usuário </elabel> <input type = "text" id = "userName"> <belt> senha </label> <input Type = "text" = "senha"> <input Type = "Button" OnClick = "Login" Login (" id = "touserpageBtn" onclick = "touserPageBtn ()"> access userpage </butut> </body> </html>Processo de back -end (Spring Boot + Spring Security + JJWT)
Ideias:
Escreva uma classe de entidade do usuário e insira uma peça de dados
Classe de entidade do usuário (usuário)
@Data @entitypublic classe usuário {@id @generatedValue private int id; nome de string privado; senha de sequência privada; @ManyTomany (cascade = {cascadeType.Refresh}, fetch = fetchtype.eager) @Jointable (name = "user_role", juncolumns = {@Joincolumn (name = "uid", referenciadoColumnName = "id")}, inversejOnJOnNe ReferencedColumnName = "Id")}) Lista privada <PPROUGE> ROUNS;} Classe de entidade de função (permissão)
@Data @entitypublic class fun {@id @generatedValue private Int ID; nome de string privado; @ManyTomany (Mappedby = "Papunos") Lista privada <suser> Usuários;} Insira dados
Tabela de usuários
| eu ia | nome | senha |
|---|---|---|
| 1 | Linyuan | 123 |
Tabela de papéis
| eu ia | nome |
|---|---|
| 1 | USUÁRIO |
Tabela user_role
| uid | livrar |
|---|---|
| 1 | 1 |
Interface da camada DAO, obtém dados através do nome de usuário e retorna um objeto opcional com um valor de java8
interface pública userRepository estende o repositório <usuário, número inteiro> {opcional <suser> findbyName (nome da string);} Escreva Logindto para transferência de dados com o front -end
@Datapublic Class Logindto implementa serializável {@NotBlank (message = "O nome de usuário não pode estar vazio") String private Nome de usuário; @NotBlank (message = "A senha não pode estar vazia") Private String senha;} Escreva uma ferramenta de geração de token e crie -a usando a biblioteca JJWT. Existem três métodos no total: gerar um token (retornar uma string), analisar o token (retornar um objeto de autenticação de autenticação) e verificar o token (retornar um valor booleano)
@ComPonentPublic Classe jwtTokenUtils {private final Logger Log = LoggerFactory.getLogger (jwttokenutils.class); Autoridades de String final privadas de sequência estática_key = "Auth"; SecretKey de String Private; // assinando a chave privada long tokenValityInMillisEconds; // data de validade privada TokenValityInMillisEcondsForreMemberMe; // (lembre -se de mim) Data de expiração @Postconstruct public void init () {this.secretkey = "linyuanmima"; int secondin1day = 1000 * 60 * 60 * 24; this.TokenValityInMillisEconds = Secondin1Day * 2L; this.TokenValityInMillisEcondsForreMemberMe = Secondin1Day * 7L; } private final estático de expiração longa estática = 432_000_000; // Crie token public String CreateToken (autenticação de autenticação, boolean RememberMe) {String Authorities = Authentication.GetaThorities (). Stream () // Obtenha a sequência de permissão do usuário, como usuário, admin .map (concessão de autora: longo agora = (new Date ()). getTime (); // Obtenha a validade atual da data do registro de data e hora; // Tempo de expiração de armazenamento se (lembre -se) {validity = nova data (agora + this.TokenValityInMillisEconds); } else {validity = nova data (agora + this.TokenValityInMillisEcondsForreMemberMe); } retornar jwts.builder () // Crie token token.setsubject (autenticação.getName ()) // Definir usuário-orientted.claim (autoridades_key, autoridades) // Adicionar permissão (signot.HerithM.Hpiration (validade) // Set Invalidation Time.Signwith (signTealGorThM. } // Obtenha permissões de usuário public autenticação getAuthentication (string token) {System.out.println ("token:"+token); Reivindicações de reivindicações = JWTS.PARSER () // Parse a carga útil do token .SetSigningKey (SecretKey) .ParSeclaimSjWs (token) .getbody (); Coleção <? estende a concessão concedida> autoridades = Arrays.stream (reivindicações.get (autoridades_key) .toString (). split (",")) // Obtenha a permissão do usuário String.map (SimpleGrantEDauthority :: new) .Collect (colecionors.tolist ()); // Converter elementos para a concessão de interface concedida Principal da coleta do usuário = novo usuário (reivindicação.getSubject (), "", autoridades); devolver o novo usuário de UsernamePasswordAthenticationToken (Principal, "", autoridades); } // Verifique se o token está correto public boolean validateToken (string token) {try {jwts.parser (). SetSigningKey (secretKey) .parseclaimsjws (token); // Verifique o token por chave de retorno true; } catch (SignatureException e) {// Log.info de exceção de assinatura ("assinatura inválida JWT."); Log.Trace ("Rastreio inválido de assinatura JWT: {}", e); } catch (malformedjwtexception e) {// JWT Erro de erro LOG.Info ("Token JWT inválido."); Log.Trace ("Token JWT inválido Trace: {}", e); } catch (expirejwtexception e) {// jwt expirou log.info ("token jwt expirado."); Log.Trace ("Token JWT expirado Trace: {}", e); } catch (não suportadojwtexception e) {// o jwt log.info ("Token JWT não suportado."); Log.Trace ("Token JWT não suportado Trace: {}", e); } catch (ilegalArgumentException e) {// O erro de exceção de erro do parâmetro log.info ("JWT Token Compact of Handler são inválidos."); Log.Trace ("JWT Token Compact of Handler são traços inválidos: {}", e); } retornar false; }} Implementar a interface do userDetails, representando a classe de entidade do usuário, é embrulhada em nosso objeto de usuário, contém permissões e outras propriedades e pode ser usado pela Spring Security
classe pública myUserDetails implementa o userDetails {Usuário privado; public myUserDetails (usuário do usuário) {this.User = user; } @Override Public Collection <? estende a concessão concedida> getAuthorities () {list <role> papes = user.getRoles (); Lista <ComedEDauthority> autoridades = new ArrayList <> (); Stringbuilder sb = new stringbuilder (); if (papes.size ()> = 1) {for (função: função: fun) {autoridades.add (new SimpleGrantEDAuthority (role.getName ())); } retornar autoridades; } retornar autoridades; } retornar autoridadeutils.ComMaseParatedStringToAuthorityList (""); } @Override public String getPassword () {return user.getpassword (); } @Override public String getUserName () {return user.getName (); } @Override public boolean isAccountNonexPired () {return true; } @Override public boolean isAccountNonLocked () {return true; } @Override public boolean iscredentialsnonexirired () {return true; } @Override public boolean isenEnabled () {return true; }} Implementar a interface UserDetailSService, que possui apenas um método para obter o UserDetails. Podemos obter o objeto do usuário do banco de dados, envolvê -lo em userDetails e devolvê -lo
@ServicePublic Classe myUserDetailSService implementa UserDetailSService {@AUTOWIRED UserRepository UserRepository; @Override public userDetails loadUserByUserName (strings s) lança UserNameNotFoundException {// Carregar o objeto de usuário do banco de dados opcional <suser> user> userRepository.findbyName (s); // Para depuração, se o valor existir, o nome do usuário e a senha serão emitidos. Ifpresent (((value)-> system.out.println ("nome de usuário:"+value.getName ()+"senha do usuário:"+value.getpassword ())); // Se o valor não estiver mais, retorne a NULL Return NewUserDetails (User.orelse (NULL)); }} Escreva um filtro. Se o usuário carregar o token, ele obterá o token e gerará um objeto de autenticação de autenticação com base no token e o armazenará no SecurityContext para controle de permissão por segurança da primavera.
classe pública jwtauthenticationTokenFilter estende o genericFilterBean {private final Logger Log = LoggerFactory.getLogger (jwtauthenticationTokenFilter.class); @Autowired Private JWTTokenutils TokenProvider; @Override Public void Dofilter (servletRequest servletRequest, servletResponse servletResponse, filterchain filterhain) lança IoException, servletexception {System.out.println ("jwtauthenticationTokenFilter"); tente {httpServletRequest httpreq = (httpServletRequest) servletRequest; String jwt = resolveteken (httpreq); if (stringutils.hastext (jwt) && this.tokenProvider.validateToken (jwt)) {// Verifique se o JWT está correto autenticação autenticação = this.tokenProvider.getauthentication (JWT); // Obter autenticação do usuário SecurityContextholder.getContext (). Setauthentication (Autenticação); // Salve o usuário no SecurityContext} filtrhain.dofilter (servletRequest, servletResponse); } catch (expirejwtexception e) {// jwt inválido log.info ("Exceção de segurança para o usuário {} - {}", e.getclaims (). getSubject (), e.getMessage ()); Log.Trace ("Exceção de segurança Trace: {}", e); ((HttpServletResponse) servletResponse) .setStatus (httpServletResponse.sc_unauthorized); }} private String ResolveToken (httpServletRequest Request) {string bearertoken = request.getheader (webcurityconfig.authorization_header); // Obtenha token do cabeçalho HTTP if (stringUtils.hastext (bearertoken) && bearertoken.startswith ("porter")) {return bearertoken.substring (7, bearertoken.length ()); // retorna a sequência de token e remova o portador} string jwt = request.getParameter (webcurityconfig.authorization_token); // Obtenha token dos parâmetros de solicitação if (stringUtils.hastext (jwt)) {return jwt; } retornar nulo; }} Escreva um Logincontroller. O usuário acessa /auth /login através do nome e senha do usuário, recebe o objeto Logindto e cria um objeto de autenticação. O código é UsernamePasswordAuthenticationToken para determinar se o objeto existe. Verifique o objeto de autenticação através do método de autenticação do AuthenticationManager. O AuthenticationManager Implementation Class ProviderManager verificará através do AuthenticationProvider (Processamento de Autenticação). O provedor padrão chama o DaoaAthenticationProvider para processamento de autenticação. O DaoauthentationProvider obterá ajustes de usuário através do userDetailSService (fonte de informação de autenticação), se a autenticação for bem -sucedida, uma autenticação contendo permissões é retornada e, em seguida, a definição do SecurityContext.
@RestControllerPublic Classe LogIncontroller {@AUTOWIRED PRIVADO PRIVENTEPOSIENTE USERRepository; @AUTOWIRED AUTHenticação privada Manager AuthenticationManager; @Autowired Private JWTTOKENULS JWTTOKENULS; @RequestMapping (value = "/auth/login", método = requestmethod.post) public string login (@Valid Logindto Logindto, httpServletResponse httpropSons) lança exceção {// criar um objeto de autenticação de autenticação através do nome de usuário e senha e implementa a classe como a usernMeationationAThationAThatication UserNamePasswordAuthenticationToken (logindto.getUserName (), logindto.getpassword ()); // Se o objeto de autenticação não estiver vazio se (objects.Nonnull (AuthenticationToken)) {userRepository.findbyName (autenticaçãoToken.getPrincipal (). ToString ()) .orelSethrow (()-> nova exceção ("User não existe"); } tente {// Verifique o objeto de autenticação através do autenticaçãoManager (padrão implementado como proverManager) autenticação = autenticação // Autenticação de ligação ao SecurityContext SecurityContextholder.getContext (). Setauthentication (autenticação); // gerar token string token = jwttokenutils.createtetoken (autenticação, false); // Escreva o token no cabeçalho HTTP httproponse.addheader (webcurityconfig.authorization_header, "portador"+token); retornar "portador"+token; } Catch (autenticação BadCredentialSexception) {THROW NOVA Exception ("Erro de senha"); }}} Escreva a classe de configuração de segurança, herde o WebSecurityConfigureRAdapter e substitua o método de configuração
@Configuration@enableWebSecurity@EnableGlobalMethodSecurity (PrePostEnabled = true) Public classe WebcurityConfig estende WebSecurityConfigRapertapter {public static final String Authorization_header = "Authorization"; public static final string autorização_token = "access_token"; @AUTOWIRED PRIVADO PRIVADO DE USERDETAILSERVICE USERDETAILSSERVICE; @Override Protected void Configure (AuthenticationManagerBuilder Auth) lança Exceção {auth // personalize para obter informações do usuário.UserDetailSService (UserDEtailSService) // Defina a senha criptografia.passwordEncoder (senhaCoder ()); } @Override Protected void Configure (httpsecurity http) lança Exceção {// Configure a solicitação de acesso à política http // fechar csrf e cors .cors (). Desabiling () .csrf (). .e () // Verifique se o http request.authorizerequests () // permite que todos os usuários acessem a página inicial e efetuem login.antmatchers ("/", "/auth/login"). Permitall () // UserSerMermission deve ser autenticado. .e () // Definir logout (). Permitall (); // Adicione o filtro JWT em http .addfilterBefore (genericFilterBean (), UserNamePasswordAtHenticationFilter.class); } @Bean Public PasswordEncoder PasswordEncoder () {Return New BCryptPasswordEncoder (); } @Bean público genérico FilterBean GenericFilterBean GenericFilterBean () {return New JwtauthenticationTokenFilter (); }} Escreva um controlador para testar
@RestControllerPublic Classe UserController {@PostMapping ("/Login") public String Login () {return "Login"; } @GetMapping ("/") public string index () {return "hello"; } @GetMapping ("/userpage") public String httpapi () {System.out.println (SecurityContextholder.getContext (). GetAuthentication (). GetPrincipal ()); retornar "Página do usuário"; } @GetMapping ("/adminpage") public string httpSuite () {return "userpage"; }}Download do código -fonte do caso (download local)
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.