Self study Shiro framework notes

Introduction to Shiro framework

1, Shiro overview

1.1 what is Shiro


Apache Shiro is a powerful and easy-to-use Java security framework, which provides functions such as authentication, authorization, encryption and session management.
Shiro can provide comprehensive security management services for any application. It can be used not only in Java se environment, but also in Java EE environment.

1.2 shiro characteristics

  • Authentication: identity authentication / login to verify whether the user has the corresponding identity;
  • Authorization: authorization, i.e. permission verification, which verifies whether an authenticated user has a certain permission. For example, verify whether a user has a certain role, or fine-grained verify whether a user has a certain permission on a resource.
  • Session Management: Session Management. After the user logs in, it is a session. Before exiting, all its information is in the session. The session can be a normal Java se environment or a Web environment.
  • Cryptography: encryption to protect the security of data. For example, the password is encrypted and stored in the database instead of plaintext;
  • Web Support: Web Support, which can be easily integrated into the web environment;
  • Caching: caching. For example, after a user logs in, it is not necessary to check his / her user information and roles / permissions every time, which can improve efficiency;
  • Concurrency: concurrency verification of multithreaded applications. If you start another thread in one thread, the permission can be automatically propagated in the past;
  • Testing: provide test support;
  • Run As: allow one user to pretend to be the identity of another user (if they allow it);
  • Remember Me: Remember Me, that is, after logging in once, you don't need to log in next time.

Three tier architecture

Subject: current user; SecurityManager: manage all users; Realm: connect data.

  1. Subject: subject, which represents the current "user". This user is not necessarily a specific person. Anything interacting with the current application is a subject, such as web crawler, robot, etc; An abstract concept; All subjects are bound to SecurityManager, and all interactions with subjects will be delegated to SecurityManager; You can think of - subject as a facade; The SecurityManager is the actual executor;
  2. SecurityManager: Security Manager; That is, all security related operations will interact with SecurityManager; And it manages all subjects; It can be seen that it is the core of Shiro. It is responsible for interacting with other components introduced later. If you have studied spring MVC, you can regard it as the front-end controller of dispatcher servlet;
  3. Realm: domain. Shiro obtains security data (such as users, roles and permissions) from realm, which means that if SecurityManager wants to verify user identity, it needs to obtain corresponding users from realm for comparison to determine whether the user identity is legal; You also need to get the corresponding role / authority of the user from realm to verify whether the user can operate; You can think of realm as a DataSource, that is, a secure data source.

2, Shiro quick start

1. Add dependency
2. Configure resources
3. Configure Java class: QuickStart java

// Get the current user object Subject
Subject currentUser = SecurityUtils.getSubject();
// Get session from current user
Session session = currentUser.getSession();
currentUser.isAuthenticated()   // Judge whether the current user is authenticated
currentUser.getPrincipal()      // Get current user role
currentUser.hasRole("schwartz") //Judge whether the current user has the "schwartz" role
currentUser.isPermitted("lightsaber:wield")       //Judge whether there is "lightsaber: wire" permission, coarse granularity
currentUser.isPermitted("winnebago:drive:eagle5") //Judge whether there is "lightsaber: wire" permission, fine-grained
currentUser.logout()            //cancellation

3, SpringBoot integrates Shiro

  1. Import dependency
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!-- mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>
<!-- log4j: Stopped change-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!-- druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>
<!-- 1.SpringBoot integration MyBatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>
<!-- 2.SpringBoot integration Shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.8.0</version>
</dependency>
<!-- 3.Thymeleaf integration Shiro -->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.1.0</version>
</dependency>
  1. MyBatis profile
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/oa?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource  #Use Druid connection pool
    #Druid data source proprietary configuration
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
mybatis:
  type-aliases-package: com.myapps.springdata.pojo
  mapper-locations: classpath:mapper/*.xml
  1. MyBatis data layer (pojo, dao=mapper, service)
//1.pojo class
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String username;
    private String password;
    private String right;
}
//2.mapper interface (dao layer)
@Repository
@Mapper
public interface IUserMapper {
    User findByName(String username);
}
//3.mapper.xml file
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapping IUserMapper Methods in interfaces-->
<mapper namespace="com.myapps.shirospringboot.mapper.IUserMapper">
    <!--call IUserMapper In the interface findByName()The result set returned by the method is User class-->
    <select id="findByName" parameterType = "String" resultType="com.myapps.shirospringboot.pojo.User">
        select * from USER_INFO where USERNAME = #{username}
    </select>
</mapper>
//4.service layer
public interface IUserService {
    User findByName(String username);
}
@Service
public class UserService implements IUserService{
    @Resource
    private IUserMapper userMapper;
    @Override
    public User findByName(String username) {
        return userMapper.findByName(username);
    }
}
  1. controller layer
@Controller
public class IndexController {
    @RequestMapping({"/","/index","index.html"})
    public String toIndex(Model model){
        model.addAttribute("msg","Springboot integration shiro");
        return "index";
    }
    @RequestMapping("/add")
    public String toAdd(){
        return "user/add";
    }
    @RequestMapping("/update")
    public String toUpdate(){
        return "user/update";
    }
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }
    @RequestMapping("/login")
    public String login(String username,String password,Model model){
        Subject currentUser = SecurityUtils.getSubject();  //Get current user
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);//Encapsulate user login data
        try {
            currentUser.login(token);   //Login performed
            return "index";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg","User name does not exist!");
            return "login";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("msg","Incorrect password!");
            return "login";
        }
    }
    @RequestMapping("/unAuth")
    @ResponseBody
    public String unAuth(){
        return "Sorry, you don't have access. Please open a member first!";
    }
}
  1. Log in to intercept ShiroConfig
@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean: Subject login interception
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean been = new ShiroFilterFactoryBean();
        been.setSecurityManager(manager);//Setting up SecurityManager
        //Add Shiro built-in filter anno: no authentication required authc: authentication required user: must have "remember me" function perms []: have the permission of a resource roles: have the permission of a role
        Map<String, String> filterMap = new LinkedHashMap<>();
        //Open the page normally when you have permission, and skip to the unauthorized page when you have no permission
        filterMap.put("/add", "perms[user:add]");
        filterMap.put("/update", "perms[user:update]");
        been.setFilterChainDefinitionMap(filterMap);
        been.setLoginUrl("/toLogin");    //Authentication failed, jump to login page
        been.setLoginUrl("/unAuth");     //Skip to unauthorized page without authorization
        return been;
    }
    //DefaultWebSecurityManager: SecurityManager
    @Bean(name="manager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("realm") UserRealm realm){
        DefaultWebSecurityManager manager= new DefaultWebSecurityManager();
        manager.setRealm(realm);          //Associated Realm
        return manager;
    }
    //Creating a realm object: Realm
    @Bean(name="realm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
    //Thymeleaf integrates Shiro
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
}
  1. Login authentication and user authorization
public class UserRealm extends AuthorizingRealm {
    @Resource
    private IUserService userService;
    //Login authentication
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("Signing in for authentication...");
        UsernamePasswordToken userToken = (UsernamePasswordToken)authenticationToken;  //Convert the authenticationToken in the parameter to the encapsulated Token type
        //Get database user (service layer)
        User user = userService.findByName(userToken.getUsername());
        if(user == null){
            return null;
        }
        //After successful login, save the user in Shiro Session for html use
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.setAttribute("loginUser", user);
        //Parameter: 1 Pass user to user for authorization (can be ""). 2 Pass the password to Shiro for verification 3 Is' '
        return new SimpleAuthenticationInfo(user,user.getPassword(),""); 
    }
    //User authorization
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("Authorizing...");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  //Authorized object
        Subject subject = SecurityUtils.getSubject();    //Get Subject
        User currentUser = (User) subject.getPrincipal();//Get the parameter user passed in login authentication
        info.addStringPermission(currentUser.getRight());//Get the permission of the current user to the authorization object
        return info;
    }
}
  1. HTML page
<!-- home page -->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h2>home page</h2>
    <div th:text="${msg}"></div>
    <hr>
    <div th:if="${session.loginUser == null}">  //Login button not displayed
        <a th:href="@{/toLogin}">Sign in</a>
    </div>
    <div shiro:hasPermission="user:add">         //Only with user:add permission will it be displayed
        <a th:href="@{/add}">add</a>
    </div>
    <div shiro:hasPermission="user:update">      //Only with user:update permission will it be displayed
        <a th:href="@{/update}">update</a>
    </div>
</body>
</html>
<!-- Login page -->
<div th:text="${msg}" style="color: red"></div>
<form th:action="@{/login}">
    <div>user name:<input type="text" name="username"></div>
    <div>password:<input type="text" name="password"></div>
    <div><input type="submit"></div>
</form>

Keywords: Java Shiro Web Security

Added by kraen123 on Mon, 14 Feb 2022 14:34:10 +0200