Spring Boot assembly Shiro

Shiro Sosuke

Official introduction

Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.

Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, encryption, and session management. With Shiro's easy-to-understand API, you can quickly and easily secure any application-from the smallest mobile application to the largest web and enterprise application.

Introduce dependencies

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.1</version>
        </dependency>

Configuration file

Configure UserRealm 主要是处理用户权限

package com.itdfq.springboot_shrio.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * @Author GocChin
 * @Date 2021/6/6 15:29
 * @Blog: itdfq.com
 * @QQ: 909256107
 * @Descript:
 */
public class UserRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权=》doGetAuthorizationInfo");
        return null;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证》doGetAuthenticationInfo");
        return null;
    }
}

Write Config 主要处理请求

package com.itdfq.springboot_shrio.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author GocChin
 * @Date 2021/6/6 15:28
 * @Blog: itdfq.com
 * @QQ: 909256107
 * @Descript:
 */
@Configuration
public class ShiroConfig {

    //ShiroFilterFacctoryBean  3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("DefaultWebSecurityManager") DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(manager);
        
        return bean;

    }

    //DafaultWebSecurityManager 2
    @Bean(name = "DefaultWebSecurityManager")                                                       //Qualifier  通过这个注解获取spring管理的 userRealm()方法
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    //创建realm对象,需要自定义类  1
    @Bean(name = "userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }



}

Login interception

  • Set login interception, and login page
 @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("DefaultWebSecurityManager") DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(manager);
        //添加shiro的内置过滤器
        /**
         * anon:无需认证就可以访问
         * authc:必须认证才能用
         * user:必须拥有记住我功能才能使用
         * perms:拥有对某个资源的权限才能访问
         * role:拥有某个角色权限才能访问
         */
        Map<String,String> filterMap = new LinkedHashMap<>();
//        filterMap.put("/user/add","authc");
//        filterMap.put("/user/update","authc");
//        上面两种写法等效于   可以采用通配符
        filterMap.put("/user/*","authc");
        bean.setFilterChainDefinitionMap(filterMap);

        //如果没有权限
        bean.setLoginUrl("/tologin");
        return bean;

    }

Login authentication

Create a login page

<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
    <p>用户名:<input type="text" name="username"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit"></p>
</form>

Login method

  @RequestMapping("/login")
    public String login(String username,String password,Model model){
        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token); //执行登录方法  如果没有异常就说明登录成功
            return "index";
        }catch (UnknownAccountException e){ //用户名不存在
            model.addAttribute("msg","用户名不存在");
            return "login";
        }catch (IncorrectCredentialsException e){//密码错误异常
            model.addAttribute("msg","密码错误");
            return "login";
        }

    }

Common exception types for official examples

 try {
    currentUser.login( token );
    //if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {  //用户名不存在
    //username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {  //密码错误
    //password didn't match, try again?
} catch ( LockedAccountException lae ) {   //账号被锁
    //account for that username is locked - can't login.  Show them a message?
}
    ... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) { 
    //unexpected condition - error?
}

Login authentication,这里采用假的用户名和密码进行测试

  //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了认证》doGetAuthenticationInfo");

        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        //用户名,密码
        String name = "root";
        String password = "123";
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        if (!userToken.getUsername().equals(name)){
            return null; //抛出异常,UnknownAccountException 用户名不存在异常
        }
        //密码认证  shiro自己做
        return new SimpleAuthenticationInfo("",password,"root");
    }

When the input user name does not exist

Username does not exist

The test username and password are incorrect

wrong password

Successful test result

Successful test result

Connect to the database to operate

Use MybatisPlus to query the database

Database table settings Users

Database Table

Modify authentication method

  //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了认证》doGetAuthenticationInfo");

        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        //从数据库中查询用户名,密码
        Users byUsername = usersService.findByUsername(userToken.getUsername());
        if (byUsername==null){
            return null;  //抛出用户不存在异常
        }
        //密码认证  shiro自己做
        return new SimpleAuthenticationInfo("",byUsername.getPwd(),"root");
    }

logout

@RequestMapping("/logout")
    public String logout(){
        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
//        设置退出登录
        subject.logout();
        return "login";
    }

Permission operation

Add permission fields to database tables

Database Table

Set page permissions

 //授权 ,没有权限会跳转到位置页面
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");

Note that the code for setting permissions here needs to be placed before setting interception to be effective

Query user permissions from the database and assign permissions

  //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权=》doGetAuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取当前登录用户
        Subject subject = SecurityUtils.getSubject();
        Users currentUser = (Users) subject.getPrincipal();//拿到User对象
        //从数据库中获取当前的权限,然后赋予当前用户的权限
        info.addStringPermission(currentUser.getPerms());
        return info;
    }
  • Demo

Shiro permission demo

Shiro and thymeleaf integration

Goals

After the root user logs in, only the add button
can be seen. After the lisi user logs in, only the update button can be seen.

Guide package

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

Add new configuration in ShiroConfig

 //整合ShiroDialect:用于整合Thymeleaf和shiro
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

Introduce namespace in html

 <html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">

html code

 <h1>首页</h1>

<a th:href="@{/tologin}">登录</a>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>|
<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">update</a>
</div>

result

result

Hide login button after login

  • The first method uses shiro tags
 <div shiro:guest="true"><a th:href="@{/tologin}">登录</a></div>
  • The second method to set the session
   //获取登录对象
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.setAttribute("loginUser",byUsername);

Front page

<div th:if="session.loginUser==null"><a th:href="@{/tologin}">登录</a></div>
  • display effect
display effect

Source address

Code Cloud Address
If it helped you, please start support