创建 3 个页面,main.html、manage.html、administrator.html

访问权限如下:

1、必须登录才能访问 main.html 2、当前用户必须拥有 manage 授权才能访问 manage.html 3、当前用户必须拥有 administrator 角色才能访问 administrator.html

1、创建 Spring Boot 应用,集成 Shiro 及相关组件,pom.xml

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-web</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-devtools</artifactId>
  13. <scope>runtime</scope>
  14. <optional>true</optional>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.projectlombok</groupId>
  18. <artifactId>lombok</artifactId>
  19. <optional>true</optional>
  20. </dependency>
  21. <!-- springboot 整合Shiro权限框架 -->
  22. <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
  23. <dependency>
  24. <groupId>org.apache.shiro</groupId>
  25. <artifactId>shiro-spring</artifactId>
  26. <version>1.7.1</version>
  27. </dependency>
  28. <!-- 数据库 -->
  29. <dependency>
  30. <groupId>mysql</groupId>
  31. <artifactId>mysql-connector-java</artifactId>
  32. </dependency>
  33. <!-- mybatis-plus -->
  34. <dependency>
  35. <groupId>com.baomidou</groupId>
  36. <artifactId>mybatis-plus-boot-starter</artifactId>
  37. <version>3.3.1.tmp</version>
  38. </dependency>
  39. <!-- shiro整合thymeleaf -->
  40. <dependency>
  41. <groupId>com.github.theborakompanioni</groupId>
  42. <artifactId>thymeleaf-extras-shiro</artifactId>
  43. <version>2.0.0</version>
  44. </dependency>
  45. <dependency>
  46. <groupId>org.springframework.boot</groupId>
  47. <artifactId>spring-boot-starter-test</artifactId>
  48. <scope>test</scope>
  49. </dependency>
  50. </dependencies>

2、自定义 Shiro 过滤器

package com.gmw.realm;

import com.gmw.entity.Account;
import com.gmw.service.AccountService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;

public class AccountRealm extends AuthorizingRealm {
    @Autowired
    private AccountService accountService;

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //UsernamePasswordToken封装了用户的所有的信息
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //获取用户输入的用户名
        String username = token.getUsername();
        //调用service方法,实现根据用户名查询用户的对象
        Account account = accountService.findByUsername(username);
        //判断用户是否存在
        if(account != null){
            /**
             * 参数1 表示通过用户名查询的用户对象
             * 参数2 表示查询到用户对象的密码
             * 参数3 getName()表示当前对象的本身
             *
             * 通过用户查询到的对象密码,和token中传入过来的密码做比较
             */
            return new SimpleAuthenticationInfo(account,account.getPassword(),super.getName());
        }
        return null;
    }

    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取当前登录的用户信息
        Subject subject = SecurityUtils.getSubject();
        Account account = (Account) subject.getPrincipal();

        //设置角色
        Set<String> roles = new HashSet<>();
        roles.add(account.getRole());
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
        //设置权限
        simpleAuthorizationInfo.addStringPermission(account.getPerms());
        return simpleAuthorizationInfo;
    }

}

3、配置类

package com.gmw.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.gmw.realm.AccountRealm;
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.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 需要将ShiroFilterFactoryBean过滤器工厂放到IOC的容器中,然后,将安全管理器注入到过滤器工厂中
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        //权限的设置
        Map<String,String> map = new HashMap<>();
        //用户访问main页面的时候,必须要登录认证
        map.put("/main","authc");
        //用户访问manage页面的时候,必须要有manage权限
        map.put("/manage","perms[manage]");
        //用户访问administrator页面的时候,必须要有administrator角色
        map.put("/administrator","roles[administrator]");

        //通过map初始化过滤器,创建三个过滤器对象
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        //设置登录的页面
        shiroFilterFactoryBean.setLoginUrl("/login");

        //设置为授权的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
        return  shiroFilterFactoryBean;
    }

    /**
     * 需要将DefaultWebSecurityManager安全管理器注入到IOC的容器中,
     * 并且,将开发者自定义的Realm注入到DefaultWebSecurityManager进行管理,才会生效
     * @return
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(accountRealm);
        return defaultWebSecurityManager;
    }

    /**
     * 需要将自定义的realm放到spring的容器中
     * @return
     */
    @Bean
    public AccountRealm accountRealm(){
        return new AccountRealm();
    }

    /**
     * 自定义thymeleaf的shiro方言
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

}

编写认证和授权规则:
**

认证过滤器

anon:无需认证。
authc:必须认证。
authcBasic:需要通过 HTTPBasic 认证。
user:不一定通过认证,只要曾经被 Shiro 记录即可,比如:记住我。

授权过滤器

perms:必须拥有某个权限才能访问。
role:必须拥有某个角色才能访问。
port:请求的端口必须是指定值才可以。
rest:请求必须基于 RESTful,POST、PUT、GET、DELETE。
ssl:必须是安全的 URL 请求,协议 HTTPS。

4、编写Serivce方法根据用户名查询用户对象

@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    @Override
    public Account findByUsername(String username) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username",username);
        return baseMapper.selectOne(wrapper);
    }
}

5、编写Controller

package com.gmw.controller;

import com.gmw.entity.Account;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AccountController {

    /**
     * 路径页面的跳转
     * @param url
     * @return
     */
    @GetMapping(value = "/{url}")
    public String redirect(@PathVariable("url") String url){
        return url;
    }

    /**
     * 用户登录操作
     * @param username
     * @param password
     * @param model
     * @return
     */
    @PostMapping("/login")
    public String login(String username, String password, Model model){
        //获取主体对象
        Subject subject = SecurityUtils.getSubject();
        //创建UsernamePasswordToken对象,用来封装用户的登录信息
        AuthenticationToken token = new UsernamePasswordToken(username,password);
        try {
            //登录
            subject.login(token);
            //登录成功后,从subject中获取Account对象
            Account account = (Account) subject.getPrincipal();
            //将登录的用户名称保存到session中
            subject.getSession().setAttribute("username",account.getUsername());
            //返回到index页面
            return "index";
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            model.addAttribute("msg","用户名错误!");
            return "login";
        } catch (IncorrectCredentialsException e){
            e.printStackTrace();
            model.addAttribute("msg","密码错误!");
            return "login";
        }
    }

    /**
     * 设置未授权的方法
     * @return
     */
    @GetMapping(value = "/unauth")
    @ResponseBody
    public String unauth(){
        return "未授权, 无法访问!";
    }

    /**
     * 退出
     * @return
     */
    @GetMapping(value = "/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "login";
    }
}

6、编写html代码


- login.html
```html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="shortcut icon" href="#"/>
</head>
<body>

    <form action="/login" method="post">
        <table>
            <tr>
                <td>用户名: </td>
                <td>
                    <input type="text" name="username"/>
                </td>
            </tr>

            <tr>
                <td>密码: </td>
                <td>
                    <input type="password" name="password"/>
                </td>
            </tr>

            <tr>
                <td colspan="2">
                    <input type="submit" value="登录"/>
                </td>
            </tr>
        </table>
    </form>
    <br/>
    <span th:text="${msg}" style="color: red;"></span>
</body>
</html>
  • main.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <link rel="shortcut icon" href="#"/>
    </head>
    <body>
      <h1>main</h1>
    </body>
    </html>
    
  • manage.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <link rel="shortcut icon" href="#"/>
    </head>
    <body>
      <h1>manage</h1>
    </body>
    </html>
    
  • admininstrator.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <link rel="shortcut icon" href="#"/>
    </head>
    <body>
      <h1>administrator</h1>
    </body>
    </html>
    

    7、Shiro整合Thymeleaf

1)pom.xml 引入依赖

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

2)配置类添加 ShiroDialect

@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>
    <h1>index</h1>
    <div th:if="${session.account != null}">
        <span th:text="${session.account.username}+'欢迎回来!'"></span><a href="/logout">退出</a>
    </div>
    <a href="/main">main</a> <br/>
    <div shiro:hasPermission="manage">
        <a href="manage">manage</a> <br/>
    </div>
    <div shiro:hasRole="administrator">
        <a href="/administrator">administrator</a>
    </div>
</body>
</html>