Writing in the front

From the previous articles, we have seen shiro’s use of authentication and authorization in general. In the following article, I will lead you to build a SpringBoot integration of Shiro’s project development scaffolding through a demo, which will string together the knowledge points learned before, and also add some content that has not been discussed before. This demo is the end of these days of learning, but also the end of the National Day Mid-Autumn festival small holiday Shiro series of special introductory articles.

SpringBoot integration Shiro thinking analysis

Analysis the authentication process

We integrated our SpringBoot application with Shiro, mainly for shiro to handle authentication and authorization for us. In other words, we need Shiro to take over the session of our SpringBoot application. Shiro authenticates and authorizes every user request. Therefore, we need to intercept the user request and forward it to Shiro for processing. This interceptor is ShiroFilter provided by Shiro.

Steps:

  1. A user initiates a request through a client (browser, mobile App, or small program)

  2. ShiroFilter intercepts the request and determines whether the requested resource is a protected resource:

    2.1 If yes, go to Step 3

    2.2 If no, go through directly

  3. Check whether the user is authenticated:

    3.1 If yes, go to Step 4

    3.2 If no, redirect user requests to the authentication page so that the user can authenticate first

  4. Obtain user permission information and compare it with the permission information required for accessing resources.

    4.1 Permit the user if the user has the access permission

    4.2 If the User does not have permissions, 403 is displayed

Database analysis and design

We use MySQL to store our authentication and permission data. User – role – authority model is used to manage user authority information dynamically.

We abstract the menus, buttons and back-end interfaces of the system into the resource data of the system. Here is the design of the database table:

At the end of the article provides the SQL script download.

Integration steps

Environment set up

maven

Create a SpringBoot Web application and introduce the following dependencies

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.6.0</version>
</dependency>
Copy the code

Add CRUD support for users, roles, and resources

Here the code is omitted, does not affect the understanding, the complete code can be downloaded from the way provided at the end of the article.

Configure Shiro

Custom Realm

/** Create a custom Realm using mysql data source *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/6 9:09 * /
public class MySQLRealm extends AuthorizingRealm {

    @Autowired
    private IUserService userService;
    @Autowired
    private IRoleService roleService;
    @Autowired
    private IResourceService resourceService;

    /** * authorization * This method is called every time a protected resource configured in ShiroConfig is accessed * therefore, caching is required, which will be modified in the next article *@param principals
     * @return* /
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        List<Role> roleList = roleService.findByUsername(username);
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        for (Role role : roleList) {
            authorizationInfo.addRole(role.getRoleName());
        }

        List<Long> roleIdList  = new ArrayList<>();
        for (Role role : roleList) {
            roleIdList.add(role.getRoleId());
        }

        List<Resource> resourceList = resourceService.findByRoleIds(roleIdList);
        for (Resource resource : resourceList) {
            authorizationInfo.addStringPermission(resource.getResourcePermissionTag());
        }

        return authorizationInfo;
    }


    /** * Authentication *@param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        if(token==null) {return null;
        }

        String principal = (String) token.getPrincipal();
        User user = userService.findByUsername(principal);
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
        returnsimpleAuthenticationInfo; }}Copy the code

Realm objects in Shiro act as data sources for authentication and authorization number information. For more custom Realm of content, please refer to my another article “introduction to Shiro learning – using a custom finish mid certification practice | gas Realm.

ShiroConfig

/** Shiro configuration class *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/6 dwells * /
@Configuration
public class ShiroConfig {

    /** * Create ShiroFilter interceptor *@return ShiroFilterFactoryBean
     */
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // Configure not intercepting paths and intercepting paths in reverse order
        HashMap<String, String> map = new HashMap<>(5);

        map.put("/authc/**"."anon");
        map.put("/login.html"."anon");
        map.put("/js/**"."anon");
        map.put("/css/**"."anon");

        map.put("/ * *"."authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        // Override the default login URL
        shiroFilterFactoryBean.setLoginUrl("/authc/unauthc");
        return shiroFilterFactoryBean;
    }

    @Bean
    public Realm getRealm(a){
        // Set the credential matcher to hash
        HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();
        // Set the algorithm
        myCredentialsMatcher.setHashAlgorithmName("md5");
        // Hash times
        myCredentialsMatcher.setHashIterations(512);
        MySQLRealm realm = new MySQLRealm();
        realm.setCredentialsMatcher(myCredentialsMatcher);
        return realm;
    }

    /** * Create a security manager for Shiro Web application *@return DefaultWebSecurityManager
     */
    @Bean
    public DefaultWebSecurityManager getSecurityManager(Realm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        SecurityUtils.setSecurityManager(securityManager);
        returnsecurityManager; }}Copy the code

In writing shiro configuration classes, it is important to note that since we are using MD5 + Salt + Hash to encrypt our passwords, we replace the default CredentialsMatcher object, For this part of the content please refer to my another article introduction to shiro learning – use MD5 encryption and salt | late practice the spirit “.

Implement authentication module

VO layer

/** Authentication request parameters *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/7 gilonite * /
@Data
public class LoginVO implements Serializable {

    private String username;
    private String password;

}
Copy the code

The web tier

/** Authentication module *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/6 10:07 * /
@RestController
@RequestMapping("/authc")
public class AuthcController {

    @Autowired
    private AuthcService authcService;

    @PostMapping("/login")
    public boolean login(@RequestBody LoginVO loginVO){

        return authcService.login(loginVO);
    }



    @GetMapping("/unauthc")
    public String unauthc(a){
        return "Please log in first."; }}Copy the code

The service layer

/ * * *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/7 15:15 * /
@Service
public class AuthcServiceImpl implements AuthcService {
    @Override
    public boolean login(LoginVO loginVO) throws AuthenticationException {
        if (loginVO==null) {return false;
        }

        if (loginVO.getUsername()==null||"".equals(loginVO.getUsername())){
            return false;
        }

        if (loginVO.getPassword() == null || "".equals(loginVO.getPassword())){
            return false;
        }
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(loginVO.getUsername(), loginVO.getPassword());

        subject.login(token);
        return true; }}Copy the code

Realization of product modules

/** Product module *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/6 "* /
@RestController
@RequestMapping("/product")
public class ProductController {

    
    @RequiresPermissions("product:get")
    @GetMapping("/get/list")
    public String getProductList(a) {
        return "productList";
    }
    
    @RequiresPermissions("product:delete")
    @GetMapping("/delete")
    public String deleteProduct(a) {
        return "Delete product Data"; }}Copy the code

For annotations to implement access control, Shiro has two main annotations: RequiresPermissions and RequiresRoles. Both apply to classes and methods. Where to use it depends on the granularity of your system permissions.

For these two annotations, there are two parameters:

  1. value: corresponds to the permission string value of permission and the role name of role respectively.
  2. logical: Logical operator. This is an enumerated type that hasANDandORTwo values. When usingANDIs required to satisfy all incomingvalueValue,ORIndicates that only one must be satisfiedvalueCan. The default isAND

More content about shiro authority (access control), you can read my another article introduction to shiro learning – Authorization (Authorization) | base early,

A simple test

The situation of passing the certification

The situation that the certification fails

Obtaining product information

Request a resource that does not have access

The default message prompt can be changed.

Access protected resources without authentication

Write in the last

In this article, we set up a preliminary SpringBoot integration Shiro application to achieve authentication and authorization.

In the next article, we will continue to refine this little demo. Add custom Shiro session management and Shiro cache. This small demo will also be upgraded to be in front – and back-separated mode.

Double section is coming to an end, code people in the arena!

If you find this article helpful, please give it a thumbs up. If there is any mistake, please feel free to comment. Here, thank you folks!

** Code and SQL download method: ** wechat search “Java development practice”, add follow and reply 20201007 to get the download link.