It only takes six steps. Springboot integrates Shiro and logs in

Time:2020-9-17

Small hub reading:

Import jar package, configure YML parameters, write shiroconfig, define default websecurity manager, rewrite realm, write controller, and write page in one go. It’s done. He’s a master~


In the previous article, we have already known the authentication and authorization process of Shiro, which is also the most core and commonly used basic function in Shiro. Now we integrate Shiro into our project and start to build a project with authentication and permission system. For example, the user center needs to log in before accessing!

1. Introduction to minimalism, analysis of Shiro’s authentication and authorization process

Integrated Shiro

According to official documents:
https://shiro.apache.org/spring-boot.html

Step 1:Integrated import jar package:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.4.2</version>
</dependency>

Some students are still using itshiro-springHowever, the integrated configuration is relatively more, so it is more convenient to use the starter package directly.

Step 2:Write the configuration, the property parameters provided by the official, and some default values. If they do not meet our requirements, you can change them.

It only takes six steps. Springboot integrates Shiro and logs in

From the configuration, we can see that Shiro’s annotation function, rememberme and other functions have been almost automatically integrated. Therefore, the starter package is still very simple to use. You only need to be familiar with Shiro’s process, so developing from 0 is not a problem.

  • application.yml
shiro:
  web:
    enabled: true
  loginUrl: /login
spring:
  freemarker:
    Suffix:. FTL ා note that the new version suffix is. Ftlh
    template-loader-path: classpath:/templates/
    settings:
      classic_ Compatible: true ා handle null values

For the above configuration, I changed the login URL, and the rest is used by default. As our simplest test, I believe you.

Step 3:Configure Shiro’s security manager and custom realm. Because the realm is responsible for our authentication and authorization, it is necessary. The custom realm must be handed over to the securitymanager for management, so these two classes need to be rewritten. Then there are some resource permission descriptions, so it is generally necessary to define shirofilterchaindefinition. Therefore, there are three classes that we often write:

  • AuthorizingRealm
  • DefaultWebSecurityManagerShiro’s core Manager
  • ShiroFilterChainDefinitionFilter chain configuration
@Configuration
public class ShiroConfig {

    @Bean
    AccountRealm accountRealm() {
        return new AccountRealm();
    }

    @Bean
    public DefaultWebSecurityManager securityManager(AccountRealm accountRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(accountRealm);
        return securityManager;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        // logged in users with the 'admin' role
        chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]");

        // logged in users with the 'document:read' permission
        chainDefinition.addPathDefinition("/docs/**", "authc, perms[document:read]");

        chainDefinition.addPathDefinition("/login", "anon");
        chainDefinition.addPathDefinition("/doLogin", "anon");

        // all other paths require a logged in user
        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }
}

Shirofilterchain definition defines the filter configuration. Let’s take a look at one of the sentences:

chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]");

This code means: access/admin/**The beginning of the link, you need to have completed login authenticationauthc, and ownadminOnly role permissions can be accessed.

You can see that the key value isLink filterThe combination of filters can be multiple at the same time. So where are authc, role, perms and anon from? What’s the special significance? What kind of interceptor?

Let’s take a look at the documentation:

It only takes six steps. Springboot integrates Shiro and logs in

As you can see, in fact, each abbreviation is the name of a filter. For example, authc stands for thisFormAuthenticationFilter。 What is the specific use of each filter? Let’s take a look at some commonly used ones

  • Authc is a form based interceptor. If there is no login, it will jump to the corresponding login page
  • User interceptor, user has been authenticated / remember that I log in
  • Anon anonymous interceptor, that is, no login is required to access
  • Roles role authorization interceptor to verify that the user has all roles
  • Perms authority authorization interceptor to verify that the user has all permissions

Step 4:OK, make project filter chain according to the resource of requirement projectShiroFilterChainDefinition。 Let’s go back to the accountrealm class. As we said before, the process of authentication and authorization is completed in realm. So we need to inherit the realm and implement two methods.

But it should be noted here that we do not generally inherit directlyRealmYou can see the real interface:

  • org.apache.shiro.realm.Realm
public interface Realm {
    String getName();

    boolean supports(AuthenticationToken token);

    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

}

From the last article, when we analyzed the source code process of authentication and authorization, you can see that the realms called by authentication and authorization areAuthenticatingRealmandAuthorizingRealm。 The source code has been encapsulated, so we can’t inherit directlyRealm, soAuthenticatingRealmandAuthorizingRealmWhich one do we inherit? We found thatAuthorizingRealmIt’s inheritanceAuthenticatingRealmSo when we rewrite the realm, we only need to integrate the superclassAuthorizingRealmThat’s fine.

public abstract class AuthorizingRealm extends AuthenticatingRealm

It only takes six steps. Springboot integrates Shiro and logs in

Therefore, with the combination of authorization and verification, as well as the caching function, we can inherit the authorizing realm when we customize the realm.

  • com.markerhub.shiro.AccountRealm
public class AccountRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    /**
     *Authorization method
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        AccountProfile principal = (AccountProfile) principalCollection.getPrimaryPrincipal();

        //Hard coding (giving user rights or roles)
        if(principal.getUsername().equals("MarkerHub")){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addRole("admin");
            return info;
        }

        return null;
    }

    /**
     *Certification method
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        AccountProfile profile = userService.login(token.getUsername(), String.valueOf(token.getPassword()));
        //Store the user information in the session to facilitate the front-end display
        SecurityUtils.getSubject().getSession().setAttribute("profile", profile);

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(profile, token.getCredentials(), getName());
        return info;
    }
}
  • com.markerhub.service.impl.UserServiceImpl
@Service
public class UserServiceImpl implements UserService {

    @Override
    public AccountProfile login(String username, String password) {

        //Todo check the database, and then match whether the password is correct!

        if(!"MarkerHub".equals(username)) {
            //Throw Shiro exception to inform users of login error information
            Throw new unknownaccountexception ("user does not exist");
        }
        if(!"111111".equals(password)) {
            Throw new incorrect credentials exception;
        }

        AccountProfile profile = new AccountProfile();
        profile.setId(1L);
        profile.setUsername("MarkerHub");
        profile.setSign (welcome to the official account MarkerHub HA).

        return profile;
    }
}

In the above code, my login method directly gives the account markerhub and assigns the role admin.

Step 5:OK, the warm-up has been completed. Next, we will write the login, exit interface and our interface:

  • com.markerhub.controller.IndexController
@Controller
public class IndexController {

    @Autowired
    HttpServletRequest req;

    @RequestMapping({"/", "/index"})
    public String index() {
        System.out.println ("logged in, accessing!! "";
        return "index";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    /**
     *Login
     */
    @PostMapping("/doLogin")
    public String doLogin(String username, String password) {

        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            SecurityUtils.getSubject().login(token);

        } catch (AuthenticationException e) {
            if (e instanceof UnknownAccountException) {
                req.setAttribute ("errormess", "user does not exist");
            } else if (e instanceof LockedAccountException) {
                req.setAttribute ("errormess", "user disabled");
            } else if (e instanceof IncorrectCredentialsException) {
                req.setAttribute ("errormess", "password error");
            } else {
                req.setAttribute ("errormess", "user authentication failed");
            }
            return "/login";
        }
        return "redirect:/";
    }


    /**
     *Log out
     */
    @GetMapping("/logout")
    public String logout() {
        SecurityUtils.getSubject().logout();
        return "redirect:/login";
    }

}

Step 6:Login page:

  • templates/login.ftl
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    < title > markerhub login < / Title >
</head>
<body>
    <h1>User login</h1>
    <h3> welcome to the official account: MarkerHub</h3>

    <form method="post" action="/doLogin">
        username: <input name="username" type="text">
        password: <input name="password" type="password">
        < input type = "submit" name = "submit" >
    </form>

    <div style="color: red;">${errorMess}</div>
</body>
</html>

Login success page:

  • templates/index.ftl
<h1>Login success:${ profile.username }</h1>
<h3>${profile.sign}</h3>

< div > < a http: // logout "> Exit < / a > < / div >

OK, we have completed the code. Next, we run the project, visit the home page, jump to the login page, and then enter the account and password, we can see that the login is completed!

Login interface:
It only takes six steps. Springboot integrates Shiro and logs in

Login success page:
It only takes six steps. Springboot integrates Shiro and logs in

Conclusion

Well, today we have done a minimalist login and registration function, and introduced the basic integration steps of Shiro. The process is very simple. Ha ha, I don’t know if you understand it.

In some load balancing scenarios, our session information needs to be shared, so Shiro and redis are generally integrated. Do you know how to integrate? Let’s talk tomorrow. Remember to come. Markerhub posts at 19:20 every day.

Attachment: demo git source code address: https://github.com/MarkerHub/…

Recommended reading:

Share a set of springboot development blog system source code, as well as the complete development document! Speed save!

GitHub’s top 100 Java open source projects, covering a variety of technology stacks!

The latest interview questions and answers in 2020

Recommended Today

How to share queues with hypertools 2.5

Share queue with swote To realize asynchronous IO between processes, the general idea is to use redis queue. Based on the development of swote, the queue can also be realized through high-performance shared memory table. Copy the code from the HTTP tutorial on swoole’s official website, and configure four worker processes to simulate multiple producers […]