shiro and Spring Boot integration (learning notes)

Posted by cypher235 on Mon, 24 Jan 2022 22:41:11 +0100

What is Shiro

Official website: http://shiro.apache.org/

It is a mainstream Java security framework that does not rely on any container and can run in Java SE and Java EE Projects. Its main function is to authenticate, authorize, session management, encryption and other operations for users accessing the system.

Shiro is a systematic framework for security management.

Shiro core components

1. UsernamePasswordToken is used by Shiro to encapsulate the user's login information and create a Token using the user's login information.

2. SecurityManager, the core part of Shiro, is responsible for security authentication and authorization.

3. Suject, an abstract concept of Shiro, contains user information.

4. Realm is a module customized by developers. According to the needs of the project, the verification and authorization logic is written in realm.

5. AuthenticationInfo, the user's role information collection, which is used for authentication.

6. Authorioninfo, the permission information collection of the role, which is used during authorization.

7. DefaultWebSecurityManager, security manager, and developer defined realms need to be injected into DefaultWebSecurityManager for management before they can take effect.

8. ShiroFilterFactoryBean is a Filter factory. Shiro's basic operation mechanism is that developers customize rules and Shiro executes them. The specific execution operations are completed by Filter objects created by ShiroFilterFactoryBean.

Shiro's operation mechanism is shown in the figure below.

Relationship between user roles and permissions:
Each user will have corresponding roles, and each role has corresponding permissions; Generally, users will not be directly associated with permissions, but have corresponding permissions through associated roles.

Spring Boot integrates Shiro

1. Create Spring Boot application, integrate Shiro and related components, POM xml

<dependencies>
    <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.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
​
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.5.3</version>
    </dependency>
​
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
​
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.1.tmp</version>
    </dependency>
​
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

2. Custom Shiro filter

public class AccountRealm extends AuthorizingRealm {
    @Autowired
    private AccountService accountService;
​
    /**
     * to grant authorization
     * @param principalCollection
     * @return
     * AuthorzationInfo,The permission information collection of the role, which is used during authorization.
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //Get current user information
        Subject subject = SecurityUtils.getSubject();
        Account account = (Account) subject.getPrincipal();
​
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //Set role
        info.addRole(account.getRole());
        //Set permissions
        info.addStringPermission(account.getPerms());
        return info;
    }
​
    /**
     * authentication
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     * AuthenticationInfo,The user's role information collection, which is used for authentication.
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        Account account = accountService.findUserByName(token.getUsername());
        //Data can be found according to the user name
        if(account != null) {
            return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
        }
        return null;
    }
}

3. Configuration class

@Configuration
public class ShiroConfig {
​
    /**
     * ShiroFilterFactoryBean:
     *In the filter factory, Shiro's basic operating mechanism is that developers customize rules and Shiro executes them,
     * The specific execution operations are completed by Filter objects created by ShiroFilterFactoryBean.
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        //Permission setting
        Map<String, String> map = new HashMap<>();
        map.put("/main","authc");
        map.put("/manage","perms[manage]");
        map.put("/administrator","roles[administrator]");
        factoryBean.setFilterChainDefinitionMap(map);
        //Set login page
        factoryBean.setLoginUrl("/login");
        //Set unauthorized page
        factoryBean.setUnauthorizedUrl("/unauthorize");
        return factoryBean;
    }
​
    /**
     *DefaultWebSecurityManager:
     * Security manager, the developer defined Realm needs to be injected into the DefaultWebSecurityManager for management before it can take effect.
     * @param accountRealm
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(@Qualifier("accountRealm") AccountRealm accountRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(accountRealm);
        return securityManager;
    }
​
    /**
     * Realm:For the module customized by the developer, the verification and authorization logic is written in real according to the needs of the project.
     * @return
     */
    @Bean
    public AccountRealm accountRealm() {
        return new AccountRealm();
    }
​
    /**
     * Integration with thymeleaf
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
}

4.Controller

@Controller
public class AccountController {
​
    @GetMapping("/{url}")
    public String redirect(@PathVariable("url") String url) {
        return url;
    }
​
    @PostMapping("/login")
    public String adminLogin( String username, String password, Model model) {
        //Create subject object
        Subject subject = SecurityUtils.getSubject();
        //Generate a token based on the user name and password
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
​
        try {
            subject.login(token);
            //After successful authentication, put the user information into the session
            Account account = (Account) subject.getPrincipal();
            subject.getSession().setAttribute("userInfo",account);
            return "index";
        } catch (Exception e) {
            e.printStackTrace();
            model.addAttribute("msg","Wrong user name or password!Please log in again");
            return "login";
        }
    }
​
    @GetMapping("/unauthorize")
    @ResponseBody
    public String unAuthorize() {
        return "The page cannot be accessed without authorization";
    }
​
    @GetMapping("/logout")
    public String LoginOut() {
        Subject subject = SecurityUtils.getSubject();
        //subject will sign us out
        subject.logout();
        return "login";
    }
​
}

Write authentication and authorization rules:

Authentication filter

anon: no certification required.

authc: must be certified.

authcBasic: http basic authentication is required.

user: you don't have to pass the certification, as long as you have been recorded by Shiro, for example: remember me.

Authorization filter

perms: you must have a permission to access.

Role: you must have a role to access.

Port: the requested port must be the specified value.

rest: the request must be based on RESTful, POST, PUT, GET and DELETE.

ssl: must be a secure URL request protocol, HTTPS.

Create 3 pages, main html,manage.html,administrator.html

Access rights are as follows:

1. You must log in to access main html

2. The current user must have manage authorization to access manage html

3. The current user must have the administrator role to access administrator html

Shiro integrates Thymeleaf

1,pom.xml import dependency

<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

2. Add ShiroDialect to the configuration class

@Bean
public ShiroDialect shiroDialect(){
    return new ShiroDialect();
}

3,index.html

<!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>Title</title>
    <link rel="shortcut icon" href="#" />
</head>
<body>
<p>index</p>
<div th:if="${session.userInfo} != null">
    <span th:text="${session.userInfo.username} + 'welcome back'"></span> <a href="/logout">cancellation</a>
</div>

<a href="/main">main</a>|
<div shiro:hasPermission="manage">
    <a href="/manage">manage</a></br>
</div>
<div shiro:hasRole="administrator">
    <a href="/administrator">administrator</a></br>
</div>

</body>
</html>

**Take notes in your study and watch carefully

Topics: Java Shiro Spring Boot security