Recentemente, encontrei um problema no projeto: o front-end e o back-end são separados, o front-end é feito com o VUE, todas as solicitações de dados usam o resistência do VUE e nenhum formulário é usado, portanto a interação de dados é usada pelo JSON, o plano de fundo usa a inicialização da mola e a verificação da permissão usa segurança na mola. Como a segurança da primavera era usada antes, eles processaram as páginas e, desta vez, eles simplesmente processaram solicitações do Ajax, então registraram alguns problemas que encontraram. A solução aqui não é apenas adequada para solicitações de AJAX, mas também resolve a verificação de solicitação móvel.
Criar um projeto
Primeiro de tudo, precisamos criar um projeto de inicialização da primavera. Ao criá -lo, precisamos introduzir Web, Spring Security, MySQL e Mybatis (a estrutura do banco de dados é realmente arbitrária, eu uso o Mybatis aqui). Após a criação, o arquivo de dependência é o seguinte:
<Depencency> <PuerpId> org.mybatis.spring.boot </groupiD> <stifactId> mybatis-spring-boot-starter </artifactid> <versão> 1.3.1 </version> </dependency> <pendency> <rugnid> org.springframework.boot </grupo> </ArtifactId> Spring-Boot-Starter-Security </ArtifactId> </Dependency> <pendence> <puperid> org.springframework.boot </foupiD> <stifactId> Spring-Boot-Starter-Web </ArtifactId> </Dependência> <Peredy> <purbroud> mysql </groupid> <TorfactId> mysql-conector-java </artifactId> <cope> Runtime </scope> </dependency> <pendencency> <voundid> commons-codec </roupidiD> <TRARFACTID> commons-codec </stifactId> <versão 1.11 </versão </dependência>
Observe que a última dependência do Commons-Codec foi adicionada manualmente por mim. Este é um projeto de código aberto do Apache que pode ser usado para gerar digestões de mensagens MD5. Vou simplesmente processar a senha no texto a seguir.
Crie um banco de dados e configure -o
Para simplificar a lógica, criei três tabelas aqui, a saber, a tabela de usuários, a tabela de papéis e a tabela de associação de função do usuário, como segue:
Em seguida, precisamos fazer uma configuração simples do nosso banco de dados no Application.Properties. Aqui estamos determinados por sua situação específica.
spring.dataSource.url = jdbc: mysql: ///vuublogspring.datasource.username=rootspring.datasource.password=123
Classe de entidade de construção
Aqui refere -se principalmente à construção de classes de usuário. As classes de usuários aqui são bastante especiais e devem implementar a interface UserDetails, como segue:
public class O usuário implementa o userDetails {Private Long ID; Nome de usuário privado de string; senha de sequência privada; apelido de string privada; Privado booleano ativado; LISTA DE PRIVADO <ROUPE> PROPULAS; @Override public boolean isAccountNonexPired () {return true; } @Override public boolean isAccountNonLocked () {return true; } @Override public boolean iscredentialsnonexirired () {return true; } @Override public boolean isenEnabled () {return habilitado; } @Override Public List <NoundedAtHority> getAuthorities () {list <CluntEDauthority> autoridades = new ArrayList <> (); para (função: funções) {autoridades.add (new SimpleGrantEDauthority ("role_" + role.getName ())); } retornar autoridades; } // getter/setter omit ...} Depois de implementar a interface do userDetails, existem vários métodos nessa interface que precisamos implementar. Os quatro métodos que retornam booleanos são todos conhecidos e conhecidos. Ativado indica se a conta do período está ativada. Este campo existe no meu banco de dados. Portanto, de acordo com os resultados da consulta, os outros retornam diretamente por períodos simples. O método GetAuthorities retorna as informações de função do usuário atual. A função do usuário são realmente os dados nas funções. Os dados nas funções são convertidos para listar <CedaDauthority> e depois retornados. Há um ponto a ser observado aqui. Como os nomes de função que eu guardo no banco de dados são todos como 'Super Administrador', 'Usuário Ordinário', etc., e não começam com caracteres como ROLE_ , então você precisa adicionar manualmente ROLE_ aqui, lembre -se.
Há também uma classe de entidade de função, que é relativamente simples e pode ser criada de acordo com os campos do banco de dados. Não vou repetir aqui.
Crie UserService
O serviço de usuários aqui também é bastante especial e é necessário implementar a interface UserDetailSService, como segue:
@ServicePublic Class UserService implementa UserDetailSService {@AUTOWIRED UserMApper UserMApper; @Autowired RolesMapper RolespApper; @Override public userDetails loadUSerByUserName (strings s) lança UserNameNotFoundException {User UserMapper.loadUserByUserName (s); if (user == null) {// evite retornar nulo, aqui retornar um objeto de usuário que não contém nenhum valor, e a verificação também falhará no processo de comparação de senha posterior retorna o novo usuário (); } // Consulte as informações da função do usuário e retorne ao usuário. LIST <PRESE> RONCES = ROLESMAPPER.GETROLSBYUID (User.getId ()); user.setRoles (funções); devolver usuário; }}Depois de implementar a interface UserDetailSService, precisamos implementar o método loadUserByUserName na interface, ou seja, consulte o usuário com base no nome de usuário. Dois mapeadores em mybatis são injetados aqui, o UserMApper é usado para consultar usuários e o Rolesmapper é usado para consultar funções. No método loadUserByUserName, primeiro consulte o usuário de acordo com os parâmetros passados (o parâmetro é o nome do usuário inserido quando o usuário efetua login). Se o usuário encontrado for nulo, você poderá lançar diretamente uma exceção de UserNameNotFoundException. No entanto, para a conveniência do processamento, devolvi um objeto de usuário sem nenhum valor. Dessa forma, durante o processo de comparação de senha subsequente, você descobrirá que o login falhou (aqui você pode ajustá -lo de acordo com as necessidades da sua empresa). Se o usuário encontrado não for nulo, consultaremos a função do usuário com base no ID do usuário e colocaremos o resultado da consulta no objeto do usuário. Este resultado da consulta será usado no método GetAuthorities do objeto do usuário.
Configuração de segurança
Vamos dar uma olhada na minha configuração de segurança primeiro e depois explicarei um por um:
@ConfigurationPublic Class webcurityConfig estende o webcurityConfigureRAdApter {@AUTOWIRED UserService UserService; @Override Protected void Configure (autenticação managerbuilder auth) lança Exceção {auth.userDetailSService (UserService) .PasswordEncoder (new PasswordEncoder () {@Override Public String Encode (Charsequence) {Return Digestutils.md5DigestShex (CharSevence Charsequence) {Return Digestutils.Md5DigShex (Charsequence). @param Charsequence PlainText* @param s } @Override Protected void Configure (httpsecurity http) lança exceção {http.authorizerequests () .antmatchers ("/admin/**"). HasRole ("super admin") .anyRequest (). in.and().formLogin().loginPage("/login_page").successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {HttpServLeartResponse.SetContentType ("Application/JSON; CHARSET = UTF-8"); .FailureHandler (new AuthenticationFailureHandler () {@Override public void onAuthenticationFailure (httpServletRequest httpServletRequest, httpServletResponse HttpSertLetResponse, autenticaçãoException e) lança ioxception, servlexception { HttpServletResponse.setContentType ("Application/JSON; }). LoginProcessingUrl ("/login") .UsernameParameter ("Nome de usuário"). PasswordParameter ("senha"). Permitall () .And (). Logout (). Permitall (). e (). Csrf (). Disable (); } @Override public void Configure (webcurity web) lança exceção {web.ignoring (). Antmatchers ("/reg"); }}Este é o núcleo de nossa configuração. Por favor me escute:
1. Primeiro de tudo, esta é uma classe de configuração; portanto, lembre -se de adicionar a anotação @Configuration. Como esta é a configuração da segurança da primavera, você deve herdar o webcurityConfigureRAdapter.
2. Injete o serviço de usuários que acaba de ser criado e nós o usaremos mais tarde.
3.Configure(AuthenticationManagerBuilder auth) method is used to configure our authentication method, and pass userService in the auth.userDetailsService() method, so that the loadUserByUsername method in the userService will be automatically called when the user logs in. The passwordEncoder behind is optional, either writeable or not, because I generated the user's plaintext password and stored it in the database, so A senha da linha simples também é necessária para processá -la ao fazer login, por isso adicionei o PasswordEncoder e, depois de adicionar a PasswordEncoder, posso diretamente novo uma classe interna anônima de senha. Existem dois métodos para implementar aqui e você pode saber o significado do método observando o nome. O primeiro método codifica obviamente criptografa o texto simples. Aqui eu uso o MD5 Message Digest. O método de implementação específico é fornecido pela dependência do Commons-Codec; O segundo método corresponde à comparação de senha, dois parâmetros, o primeiro parâmetro é a senha de texto simples e o segundo é o CipherText. Aqui, você só precisa criptografar o texto simples e compará -lo com o texto cifra (se estiver interessado nisso, poderá continuar adicionando sal à senha).
4.Configure (httpsecurity http) é usado para configurar nossas regras de autenticação, etc. O método Autorizerequests significa que a configuração de regra de autenticação está ativada, Antmatchers ("/admin/**"). HasRole ("Super Administrador" significa que o caminho de /admin/** precisa ser acordado "). Vi na Internet que meus amigos têm dúvidas sobre adicionar ROLE_ no método HasRole. Não adicione aqui. Se você usa o método HasAuthority, precisará adicioná -lo. anyrequest (). autenticado () significa que todos os outros caminhos precisam ser autenticados/logados antes de acessar. Em seguida, configuramos a página de login como login_page, o caminho de processamento de login é /login, o nome de usuário de login é o nome de usuário e a senha é a senha. Configuramos esses caminhos para acessar diretamente e o login também pode ser acessado diretamente e, finalmente, fechar o CSRF. Em SuccessHandler, use a resposta para retornar o JSON que fez login com sucesso. Lembre -se de não usar o padrãoSuccessurl. O padrãoSuccessUrl é uma página que é redirecionada somente após o login com sucesso. O mesmo motivo também é usado para falhas.
5. Configurei algumas regras de filtragem no método de configuração (WebSecurity Web), para não entrar em detalhes.
6. Além disso, para arquivos estáticos, como /images/** , /css/** e /js/** , o padrão não é interceptado.
Controlador
Por fim, vamos dar uma olhada no nosso controlador, como segue:
@RestControllerPublic Classe LoginGontroller {/** * Se você pular automaticamente para esta página, significa que o usuário não está conectado e você pode retornar o prompt correspondente * <p> * Se você deseja suportar o login, você pode julgar o tipo de solicitação neste método e depois voltar a json ou " * * * * * * * * * * * * * * * *, você pode apoiar o login, pode julgar o tipo de login * * * * * * * * * * * * * *, você pode apoiar o login, pode julgar o tipo de login; Respbean loginpage () {return New Respbean ("Erro", "ainda não está conectado, faça o login!"); }} No geral, esse controlador é relativamente simples. O Response retorna um JSON simples, que não é detalhado. O que você precisa prestar atenção aqui é login_page . A página de login que configuramos é um login_page . Mas, de fato, login_page não é uma página, mas um JSON. Isso ocorre porque, quando visito outras páginas sem fazer login, a segurança da primavera pulará automaticamente para a página login_page . No entanto, no pedido de Ajax, esse tipo de salto não é necessário. Tudo o que eu quero é um rápido para fazer login ou não, para que eu possa devolver o JSON aqui.
teste
Por fim, os amigos podem usar ferramentas como Postman ou Restclient para testar problemas de login e permissão, para que eu não o demonstre.
OK, após a introdução acima, acredito que os amigos já entenderam as solicitações de login do Ajax Spring Boot+Spring Security. Ok, isso é tudo para este artigo. Se você tiver alguma dúvida, deixe uma mensagem para discutir.