Você não conhecerá Shiro em 2021? -6. Integração de Shiro e Spring Boot


Prefácio: Os
frameworks java mais populares são SpringCloud e SpringBoot. Este artigo resume o uso integrado de Shiro e SpringBoot e cria uma função de login simples. Porém, a função de login ainda não utiliza o banco de dados, e os dados são simulados.O próximo artigo irá resumir Shiro + JSP + SpringBoot + MyBatis + mysql para perceber o cenário real de autenticação e autorização.

1. Processo de integração

Em primeiro lugar, precisamos deixar claro o propósito do uso do Shiro, que é evitar que alguns dos recursos do sistema sejam acessados ​​aleatoriamente, ou interfaces, ou recursos estáticos. Precisamos interceptar as solicitações de acesso a tais recursos. Como para interceptá-lo, fornecido em Shiro. Como o ShiroFilter é usado para interceptar essas solicitações de acesso a recursos restritos, só precisamos injetar o ShiroFilter no contêiner Spring e, em seguida, configurar quais solicitações o ShiroFilter precisa filtrar podem ser usadas normalmente. Como muitas solicitações precisam ser filtradas por Shiro antes de serem acessadas, também existem alguns recursos que não precisam ser interceptados, como nossa página de login, página de registro ou página de navegação do produto no shopping, página de detalhes do produto, etc. ., que não pode ser interceptada., Esta parte é o recurso público. Devemos liberar recursos públicos. Vamos dar uma olhada em como implementar a integração de Shiro e SpringBoot passo a passo.
Insira a descrição da imagem aqui

1. Use o Spring initializr para criar o projeto SpringBoot

Conforme mostrado na figura abaixo, selecionamos esta função em Arquivo–> Novo–> Projeto, e então podemos criar rapidamente um projeto SpringBoot. Se não houver jar necessário no armazém padrão, usaremos o endereço selecionado na imagem para baixar o modelo.
Insira a descrição da imagem aqui

2. Selecione a versão jdk, selecione o lançador

Ao criar um projeto, preste atenção a estas duas escolhas. Você pode começar com o nome do projeto. Depende do seu humor. Você pode escolher o JDK que foi instalado em seu próprio ambiente de desenvolvimento. No entanto, usando a função Sping initializr, JDK7 pode não suportá-lo, de preferência JDK8 ou JDK11, essas duas são as principais versões do JDK que ainda estão sendo atualizadas e as demais versões abaixo do JDK16 pararam de atualizar.
Insira a descrição da imagem aqui
Em seguida, precisamos selecionar o iniciador de que precisamos. O iniciador aqui só precisa selecionar o iniciador que suporta JSP, a propósito, escolha lombok, que é a dependência de suportar anotações de entidade de classe. Além disso, precisamos selecionar o iniciador para desenvolvimento web, conforme mostrado na figura abaixo.

Insira a descrição da imagem aqui

3. Crie um projeto de inicialização de página jsp

Neste ponto, nós realmente criamos um projeto da Web. Se a rede não estiver boa, precisamos esperar um pouco para que o projeto baixe o pacote jar. Após a conclusão do download da dependência, precisamos criar várias páginas jsp para interagir com a interface de segundo plano. Criamos uma pasta webapp em main para armazenar arquivos de página jsp.
Insira a descrição da imagem aqui
Insira a descrição da imagem aqui
Criamos uma nova página login.jsp, o conteúdo da página é o seguinte, que também é o modelo padrão do jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>标题</title>
    <style type="text/css">
        *{
    
    margin: 0;padding: 0;}
        form{
    
    margin: 0 auto;padding:15px; width: 300px;height:300px;text-align: center;}
        #submit{
    
    padding: 10px}
        #submit input{
    
    width: 50px;height: 24px;}
    </style>
</head>
<body>
    <h1>登录页</h1>

    <form action="${pageContext.request.contextPath}/user/login" method="post">
        用户名:<input type="text" name ="username"/><br/>
        密 码 :<input type="text" name ="password"/><br/>
        <input type="submit" value="登录"><br/>
    </form>
</body>
</html>

Crie outra página index.jsp para exibição após o login bem-sucedido. Como mostrado abaixo:

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>标题</title>
    <style type="text/css">
        *{
    
    margin: 0;padding: 0;}
        form{
    
    margin: 0 auto;padding:15px; width: 300px;height:300px;text-align: center;}
        #submit{
    
    padding: 10px}
        #submit input{
    
    width: 50px;height: 24px;}
    </style>
</head>
<body>
    <h1>系统主页</h1>
    <ul>
        <a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
        <li><a href="">用户管理</a></li>
        <li><a href=""></a>商品管理</li>
        <li><a href=""></a>商户管理</li>
        <li><a href=""></a>内容管理</li>
    </ul>
</body>
</html>

Depois que a página jsp for criada, vamos configurar o arquivo de configuração application.yml, configurar a porta de serviço, o nome do serviço e configurar o arquivo front-end para suportar jsp, conforme mostrado abaixo:

# 服务端口
server.port=8888
# 项目访问路径
server.servlet.context-path=/shiro
# 项目名
spring.application.name=shiro

# 配置mvc的视图解析器支持jsp,默认不支持
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

4. Inicie o projeto para visualizar a página de login, e a configuração depende do login bem-sucedido

Insira a descrição da imagem aqui
Pode-se ver na figura que nosso projeto iniciou normalmente. Se não iniciar normalmente, você pode editar as informações de configuração do projeto e alterar o diretório de trabalho. Altere o valor para a figura a seguir e reinicie-o. normal.
Insira a descrição da imagem aqui
Este é o nosso projeto que começou normalmente e, em seguida, adicionou a dependência de Shiro e JSP. Do seguinte modo:

<!--引入支持jsp的依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!--引入支持jstl的依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <!--引入支持shiro的启动器-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.5.3</version>
        </dependency>

5. Crie um controlador, personalize o Realm e injete outros objetos, como o Realm, no contêiner Spring

Precisamos fornecer um controlador simples com métodos de login e logout. Para a chamada de jsp. código mostrado abaixo:

@Controller
@RequestMapping("/user")
public class LoginController {
    
    

    @PostMapping("/login")
    public String login(String username,String password){
    
    
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
        Subject subject = SecurityUtils.getSubject();
        try {
    
    
            subject.login(usernamePasswordToken);
        } catch (UnknownAccountException e) {
    
    
            e.printStackTrace();
            System.out.println("用户名错误");
            return "redirect:/login.jsp";
        } catch(IncorrectCredentialsException e){
    
    
            e.printStackTrace();
            System.out.println("密码错误");
            return "redirect:/login.jsp";
        }
        return "redirect:/index.jsp";
    }

    @RequestMapping("logout")
    public String logout(){
    
    
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login.jsp";
    }
}

Depois que o controlador é escrito, também precisamos escrever nosso próprio Realm. Todos nós sabemos que a fonte de dados de Shiro é Realm, então devemos implementar um Realm para obter informações de autenticação e autorização por nós mesmos para realizar nosso próprio login. A parte para realizar o autorização é apenas a operação do processo de autenticação primeiro, mas o método MD5 + salt + hash ainda é usado para criptografar a senha durante a autenticação (o artigo anterior já falou sobre como fazer isso e vou escrever aqui).

public class FirstRealm extends AuthorizingRealm {
    
    

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
    
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
    
        System.out.println("进入认证方法");
        String username = (String)authenticationToken.getPrincipal();
        Md5Hash md5Hash = new Md5Hash("123","234@#$",1024);
        if(username.equals("luban")){
    
    
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("luban",md5Hash.toString(), ByteSource.Util.bytes("234@#$"),this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

Depois que a definição do Realm for concluída, precisamos injetar o Realm personalizado no contêiner Spring. Podemos injetar o objeto no contêiner. Podemos implementá-lo no arquivo xml ou usar anotações. O mais comumente usado é implementá-lo por meio do classe de configuração, ou seja, definimos uma classe de configuração para injetar objetos no container Spring através da anotação @Bean, conforme mostrado abaixo:

@Configuration
public class ShiroConfig {
    
    

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
    
    
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String,String> map = new HashMap<>();
        map.put("/user/login","anon");//表示该资源无需认证授权,无需授权的应该写在上面
        map.put("/user/logout","anon");//表示该资源无需认证授权
        map.put("/login.jsp","anon");//表示该资源无需认证授权
       
        map.put("/**","authc");//表示所有资源都需要经过认证授权
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        //设置授权失败返回的页面
        shiroFilterFactoryBean.setLoginUrl("login.jsp");//这也是默认值
        return shiroFilterFactoryBean;
    }

    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(FirstRealm firstReaml){
    
    
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(firstReaml);
        return defaultWebSecurityManager;
    }

    @Bean
    public FirstRealm getRealm(){
    
    
        FirstRealm firstRealm = new FirstRealm();
        Md5CredentialsMatcher md5CredentialsMatcher = new Md5CredentialsMatcher();
        md5CredentialsMatcher.setHashIterations(1024);
        firstRealm.setCredentialsMatcher(md5CredentialsMatcher);
        return firstRealm;
    }

}

Conforme mostrado acima, ShiroFilter é obtido através de ShiroFilterFactoryBean, a fábrica de ShiroFilter, e então injeta SecurityManager para ele. O SecurityManager aqui é DefaultWebSecurityManager, que é um gerenciador de segurança para a versão web. Você pode usá-lo para desenvolvimento web. No reino personalizado de FirstRealm, precisamos personalizar um matcher de senha para coincidir com a senha criptografada por MD5 + salt + hash, e dizer ao matcher quantas vezes nossa senha foi hash, e então está tudo bem.
Os parâmetros de configuração suportados no ShiroFilter e seus significados correspondentes estão listados abaixo. Normalmente, os dois primeiros são usados.
Insira a descrição da imagem aqui

6. Inicie o projeto e teste a função de login.

Neste ponto, nossa função de login implementada por Shiro + SpringBoot usando MD5 + salt + hash está concluída. A seguir, vamos testar. A conta que usamos é luban: 123. Vamos usar luban: 1234 para fazer login e testar.

Insira a descrição da imagem aqui
Então descobri que o login falhou e voltei para a página de login, porque quando configuramos o ShiroFilter, a página de retorno para falha de autenticação era login.jsp, então voltei para cá.
O back-end relatou o seguinte erro, que obviamente é um erro de senha.
Insira a descrição da imagem aqui
Em seguida, usamos a conta luban: 123 para fazer login e testar, os resultados são os seguintes:
Insira a descrição da imagem aqui

Podemos descobrir que entramos no sistema normalmente, o que verifica se a função de login que implementamos usando Shiro + SpringBoot está completamente normal.

2. Problemas e reflexões na integração

Podemos encontrar vários problemas durante a integração. Aqui estão alguns dos problemas e pensamentos que encontrei durante o processo de integração.

1. Problema de configuração do caminho do filtro ShiroFilter

Ao configurar os caminhos que precisamos filtrar e os caminhos que precisam ser excluídos, algumas pessoas dizem que os caminhos dos recursos públicos precisam ser configurados na parte superior, e os recursos restritos são configurados abaixo, assim

Map<String,String> map = new HashMap<>();
map.put("/user/login","anon");//表示该资源无需认证授权,无需授权的应该写在上面
map.put("/user/logout","anon");//表示该资源无需认证授权
map.put("/login.jsp","anon");//表示该资源无需认证授权
map.put("/**","authc");//表示所有资源都需要经过认证授权
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

No entanto, após o teste, colocar o código de recursos de filtragem no topo dos recursos públicos pode ser usado normalmente. Claro, as alterações de versão não são excluídas para corrigir este problema. No entanto, a configuração de recursos públicos e recursos restritos após Shiro 1.5.3 usado no teste atual definitivamente não é necessário considerar o pedido.

2. Configure login.jsp como uma página de exibição para recursos restritos, mas não conectado. Você ainda precisa configurar login.jsp como um recurso público?

Algumas pessoas dizem que se o acesso ao recurso restrito for configurado, ele será redirecionado para a página de login, e então não há necessidade de definir o identificador de recurso público para a página de login. No ShiroFilter, podemos ver que a demonstração que escrevi atualmente configura a página de login como Para recursos públicos, quando eu testar este cenário, se especificarmos apenas a página de exibição em que o recurso restrito não está conectado, relataremos esse problema quando acessarmos o recurso restrito index.jsp, conforme mostrado abaixo :
Insira a descrição da imagem aqui
Este erro já foi mencionado na figura. É causado pelo número excessivo de redirecionamentos do sistema. Ao acessar index.jsp, redirecionamos para login.jsp sem efetuar login no sistema. Então, descobrimos que login.jsp era não é um recurso público e continuou a redirecionar login.jsp em um loop infinito. Portanto, temos que configurar login.jsp como um recurso público. Então foi normal. do seguinte modo:

map.put("/login.jsp","anon");//表示该资源无需认证授权

3. Efetue logout anormalmente após login bem-sucedido, efetue login no sistema novamente, e o nome de usuário e senha incorretos também entrarão no sistema

Claro, o código mostrado aqui não tem esse problema. Depois que descobri esse problema, eu modifiquei o código, principalmente este pedaço de código.

 @PostMapping("/login")
  public String login(String username,String password){
    
    
      UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
      Subject subject = SecurityUtils.getSubject();
      try {
    
    
          subject.login(usernamePasswordToken);
      } catch (UnknownAccountException e) {
    
    
          e.printStackTrace();
          System.out.println("用户名错误");
          return "redirect:/login.jsp";
      } catch(IncorrectCredentialsException e){
    
    
          e.printStackTrace();
          System.out.println("密码错误");
          return "redirect:/login.jsp";
      }
      return "redirect:/index.jsp";
  }

Vamos falar sobre o motivo desse erro. Quando o nome de usuário ou a senha de login do usuário estão errados, o programa o detecta e, em seguida, lança uma exceção. Se o programa continuar a ser executado, ele retornará à página inde.jsp e porque ele não saiu normalmente, primeiro Após o segundo login, Shiro irá armazenar em cache as informações de login, para que possamos entrar na página index.jsp normalmente.Nós só precisamos retornar para login.jsp diretamente quando ocorrer o erro de captura.

Três. Resumo

Este artigo resume a integração de Shiro e SpringBoot. A ideia também é muito clara. Primeiro introduza as dependências de Shiro, depois personalize o Realm e, em seguida, injete Realm e Filtro, SecurityManager e outros objetos por meio da classe de configuração e, em seguida, melhore a interface de login, faça o login Página, e a página inicial de login, etc. Finalmente, analisei vários problemas que podem ser encontrados e espero que seja útil para os amigos de passagem.

Acho que você gosta

Origin blog.csdn.net/m0_46897923/article/details/115052438
Recomendado
Clasificación