Will you not know Shiro in 2021?-6. Integration of Shiro and Spring Boot


Preface: The
most popular java frameworks are SpringCloud and SpringBoot. This article summarizes the integrated use of Shiro and SpringBoot, and makes a simple login function. However, the login function still does not use the database, and the data is simulated. The next article will summarize Shiro+JSP+SpringBoot+MyBatis+mysql to realize the real scenario of authentication and authorization.

1. Integration process

First of all, we need to make clear the purpose of using Shiro, which is to prevent some of the resources in the system from being accessed randomly, or interfaces, or static resources. We need to intercept requests for access to such resources. How to intercept it, provided in Shiro Since ShiroFilter is used to intercept these requests for access to restricted resources, we only need to inject ShiroFilter into the Spring container, and then configure which requests that ShiroFilter needs to filter can be used normally. Since many requests need to be filtered by Shiro before they can be accessed, there are also some resources that do not need to be intercepted, such as our login page, registration page, or product browsing page of the mall, product details page, etc., which cannot be intercepted. , This part is the public resource. We should release public resources. Let's take a look at how to implement the integration of Shiro and SpringBoot step by step.
Insert picture description here

1. Use Spring initializr to create SpringBoot project

As shown in the figure below, we select this function under FIle–>New–>Project, and then we can quickly create a SpringBoot project. If there is no required jar in the default warehouse, we will use the address selected in the picture to download the template.
Insert picture description here

2. Select the jdk version, select the launcher

When creating a project, pay attention to these two choices. You can start with the project name. It depends on your mood. You can choose the JDK that has been installed in your own development environment. However, using the Sping initializr function, JDK7 may not support it, preferably JDK8 or JDK11, these two are the major versions of the JDK that are still being updated, and the rest of the versions below JDK16 have stopped updating.
Insert picture description here
Then we need to select the launcher we need. The launcher here only needs to select the launcher that supports JSP, by the way, choose lombok, which is the dependency of supporting class entity annotations. In addition, we need to select the launcher for web development, as shown in the figure below.

Insert picture description here

3. Create a jsp page startup project

At this point, we have actually created a web project. If the network is not good, we need to wait quietly for a while for the project to download the jar package. After the dependency download is complete, we need to create several jsp pages to interact with the background interface. We create a webapp folder under main to store jsp page files.
Insert picture description here
Insert picture description here
We create a new login.jsp page, the content of the page is as follows, which is also the standard template of 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>

Create another index.jsp page for display after successful login. As shown below:

<%@ 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>

After the jsp page is created, let's configure the application.yml configuration file, configure the service port, service name, configure the front-end file to support jsp, etc., as shown below:

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

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

4. Start the project to view the login page, and the configuration depends on the successful login

Insert picture description here
It can be seen from the figure that our project has started normally. If it does not start normally, you can edit the configuration information of the project and change the Working directory. Change the value to the following figure, and then restart it will be normal.
Insert picture description here
This is our project has started normally, and then added Shiro and JSP dependency. As follows:

<!--引入支持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. Create a controller, customize Realm, and inject other objects such as Realm into the Spring container

We need to provide a simple controller with login and logout methods. For the call of jsp. code show as below:

@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";
    }
}

After the controller is written, we also need to write our own Realm. We all know that Shiro's data source is Realm, so we must implement a Realm to obtain authentication and authorization information by ourselves to realize our own login. The part to realize the authorization is only the authentication process operation first, but the MD5+salt+hash method is still used to encrypt the password during authentication (the previous article has already talked about how to achieve it, and I will write it here).

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;
    }
}

After the Realm definition is completed, we need to inject the custom Realm into the Spring container. We can inject the object into the container. We can implement it in the xml file or use annotations. The most commonly used is to implement it through the configuration class, that is, we Define a configuration class to inject objects into the Spring container through @Bean annotation, as shown below:

@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;
    }

}

As shown above, ShiroFilter is obtained through ShiroFilterFactoryBean, the factory of ShiroFilter, and then injects SecurityManager for him. The SecurityManager here is DefaultWebSecurityManager, which is a security manager for web version. You can use it for web development. In the custom Realm of FirstRealm, we need to customize a password matcher to match the password encrypted by MD5+salt+hash, and tell the matcher how many times our password has been hashed, and then it’s fine.
The configuration parameters supported in ShiroFilter and their corresponding meanings are listed below. Usually, the top two are used.
Insert picture description here

6. Start the project and test the login function.

At this point, our login function implemented by Shiro+SpringBoot using MD5+salt+hash is complete. Next, let’s test it. The account we use is luban:123. Let’s use luban:1234 to log in and test.

Insert picture description here
Then I found that the login failed and jumped back to the login page. This is because when we configured ShiroFilter, the return page for authentication failure was login.jsp, so I jumped back here.
The backend reported the following error, which is obviously a password error.
Insert picture description here
Then we used the account luban:123 to log in and test, the results are as follows:
Insert picture description here

We can find that we have entered the system normally, which verifies that the login function we implemented using Shiro+SpringBoot is completely normal.

2. Problems and Thoughts in Integration

We may encounter various problems during the integration. Here are some of the problems and thoughts I encountered during the integration process.

1. ShiroFilter filter path configuration problem

When configuring the paths that we need to filter and the paths that need to be excluded, some people say that the paths of public resources need to be configured on the top, and the restricted resources are configured below, like this

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);

However, after testing, putting the code of filtering resources on top of public resources can be used normally. Of course, version changes are not excluded to fix this problem. However, configuring public resources and restricted resources after Shiro 1.5.3 used in the current test is definitely not Need to consider the order.

2. Configure login.jsp as a display page for restricted resources but not logged in. Do you still need to configure login.jsp as a public resource?

Some people say that if the restricted resource access is configured, it will be redirected to the login page, and then there is no need to set the public resource identifier for the login page. In ShiroFilter, we can see that the demo I wrote currently configures the login page as For public resources, when I test this scenario, if we only specify the display page where the restricted resource is not logged in, then we will report this problem when we access the restricted resource index.jsp, as shown below:
Insert picture description here
This error has already been mentioned in the figure. It is caused by the excessive number of system redirects. When accessing index.jsp, we redirected to login.jsp without logging in to the system. Then, we found that login.jsp was not a public resource and continued to redirect login.jsp into an endless loop. So here we have to configure login.jsp as a public resource. Then it was normal. as follows:

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

3. Log out abnormally after successful login, log in to the system again, and the wrong user name and password will also enter the system

Of course, the code shown here does not have this problem. After I discovered this problem, I have modified the code, mainly this piece of code.

 @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";
  }

Let’s talk about the reason for this error. When the user’s login username or password is wrong, the program will catch it, and then throw an exception. If the program continues to execute, it will return to the inde.jsp page, and because it did not exit normally, first After the second login, Shiro will cache the login information, so we can enter the index.jsp page normally. We only need to return to login.jsp directly when the catch error occurs.

Three. Summary

This article summarizes the integration of Shiro and SpringBoot. The idea is also very clear. First introduce Shiro's dependencies, then customize Realm, and then inject Realm and Filter, SecurityManager and other objects through the configuration class, and then improve the login interface, login Page, and the login homepage, etc. Finally, I analyzed several problems that may be encountered, and I hope it will be helpful to friends passing by.

Guess you like

Origin blog.csdn.net/m0_46897923/article/details/115052438
Recommended