Spring Boot 整合 Shiro 實現登陸認證

安全無處不在,趁着放假讀了一下 Shiro 文檔,並記錄一下 Shiro 整合 Spring Boot 在數據庫中根據角色控制訪問權限html

簡介

Apache Shiro是一個功能強大、靈活的,開源的安全框架。它能夠乾淨利落地處理身份驗證、受權、企業會話管理和加密。
Shiro Architecturejava

上圖是 Shiro 的基本架構mysql

Authentication(認證)

有時被稱爲「登陸」,用來證實用戶是用戶他們本身本人算法

Authorization(受權)

訪問控制的過程,即肯定「誰」訪問「什麼」spring

Session Management(會話管理)

管理用戶特定的會話,在 Shiro 裏面能夠發現全部的用戶的會話信息都會由 Shiro 來進行控制sql

Cryptography(加密)

在對數據源使用加密算法加密的同時,保證易於使用數據庫

Start

環境

Spring Boot 1.5.9
MySQL 5.7
Maven 3.5.2
Spring Data Jpa
Lombokapache

添加依賴

這裏只給出主要的 Shiro 依賴安全

<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.4.0-RC2</version>
        </dependency>

配置

咱們暫時只須要用戶表、角色表,在 Spring boot 中修改配置文件將自動爲咱們建立數據庫表架構

server:
  port: 8888
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/shiro?characterEncoding=utf-8&useSSL=false
  jpa:
    generate-ddl: true
    hibernate:
      ddl-auto: update
    show-sql: true

實體

Role.java

@Data
@Entity
public class Role {

    @Id
    @GeneratedValue
    private Integer id;

    private Long userId;

    private String role;

}

User.java

@Data
@Entity
public class User {
    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private String password;

}

Realm

首先創建 Realm 類,繼承自 AuthorizingRealm,自定義咱們本身的受權和認證的方法。Realm 是能夠訪問特定於應用程序的安全性數據(如用戶,角色和權限)的組件。

Realm.java

public class Realm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    
    //受權
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //從憑證中得到用戶名
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        //根據用戶名查詢用戶對象
        User user = userService.getUserByUserName(username);
        //查詢用戶擁有的角色
        List<Role> list = roleService.findByUserId(user.getId());
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        for (Role role : list) {
            //賦予用戶角色
            info.addStringPermission(role.getRole());
        }
        return info;
    }

    //認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //得到當前用戶的用戶名
        String username = (String) authenticationToken.getPrincipal();

        //從數據庫中根據用戶名查找用戶
        User user = userService.getUserByUserName(username);
        if (userService.getUserByUserName(username) == null) {
            throw new UnknownAccountException(
                    "沒有在本系統中找到對應的用戶信息。");
        }

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName());
        return info;
    }

}

Shiro 配置類

ShiroConfig.java

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //如下是過濾鏈,按順序過濾,因此/**須要放最後
        //開放的靜態資源
        filterChainDefinitionMap.put("/favicon.ico", "anon");//網站圖標
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(myRealm());
        return defaultWebSecurityManager;
    }

    @Bean
    public MyRealm myRealm() {
        MyRealm myRealm = new MyRealm();
        return myRealm;
    }
}

控制器

UserController.java

@Controller
public class UserController {

    @Autowired
    private UserService userService;

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

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

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

    @PostMapping("/login")
    public String doLogin(String username, String password) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "redirect:admin";
    }

    @GetMapping("/home")
    public String home() {
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.checkPermission("admin");
        } catch (UnauthorizedException exception) {
            System.out.println("沒有足夠的權限");
        }
        return "home";
    }

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

Service

UserService.java

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public User getUserByUserName(String username) {
        return userDao.findByUsername(username);
    }

    @RequiresRoles("admin")
    public void send() {
        System.out.println("我如今擁有角色admin,能夠執行本條語句");
    }

}

展現層

admin.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en"/>
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
</head>
<body>

<form action="/login" method="post">
    <input type="text" name="username" />
    <input type="password" name="password" />
    <input type="submit" value="登陸" />
</form>

</body>
</html>

home.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en"/>
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
</head>
<body>
home
</body>
</html>

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en"/>
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
</head>
<body>
index
<a href="/login">請登陸</a>
</body>
</html>

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en"/>
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
</head>
<body>

<form action="/login" method="post">
    <input type="text" name="username" />
    <input type="password" name="password" />
    <input type="submit" value="登陸" />
</form>

</body>
</html>

總結

這個小案例實現了根據角色來控制用戶訪問,其中最重要的就是 Realm,它充當了Shiro與應用安全數據間的「橋樑」或者「鏈接器」

相關文章
相關標籤/搜索