Spring Boot2整合Shiro(1):身份認證
Spring Boot2整合Shiro(1):身份認證
前言
本文主要介紹了在Spring Boot2專案中整合Shiro實現登入認證。本文假設讀者已經對Shiro和基於RBAC的許可權控制系統有了基本的認識。
本專案沒有資料庫,也就沒有dao層,所有的使用者和密碼均在Service層採用硬編碼。
特別提醒:因為程式碼塊中的
@
符號在部落格釋出過程中會導致程式碼格式混亂,所以@
都是用雙斜槓註釋了。
建立工程
通過idea的Spring Initializr
新建工程spring-boot2-shiro-project
,選擇web模組即可。在專案根目錄新增service
controller
和config
資料夾,專案目錄如下圖所示: 封裝統一的返回值
這一步對於整合shiro不是必須的
為了在專案中返回統一的結果,我們新建一個泛型類,包含屬性:結果程式碼code
、結果資訊message
和結果資料data
,其中data採用泛型,程式碼如下:
public class Result<T> { private ResultCodeEnum code; private String message; private T data; public ResultCodeEnum getCode() { return code; } public Result() { } public Result setCode(ResultCodeEnum resultCode) { this.code = resultCode; return this; } public String getMessage() { return message; } public Result setMessage(String message) { this.message = message; return this; } public T getData() { return data; } public Result setData(T data) { this.data = data; return this; } }
再新建一個列舉類ResultCodeEnum
,列舉所有的返回程式碼code
:
public enum ResultCodeEnum { SUCCESS(200),//成功 FAIL(400),//失敗 UNAUTHORIZED(401),//未認證(簽名錯誤) NOT_FOUND(404),//介面不存在 INTERNAL_SERVER_ERROR(500);//伺服器內部錯誤 public int code; ResultCodeEnum(int code) { this.code = code; } public int getCode() { return this.code; } }
再建一個結果生成類ResultGenerator
,減少重複程式碼,保證返回值的統一性
public class ResultGenerator {
private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";
//成功
public static Result genSuccessResult() {
return new Result()
.setCode(ResultCodeEnum.SUCCESS)
.setMessage(DEFAULT_SUCCESS_MESSAGE);
}
public static <T> Result<T> genSuccessResult(T data) {
return new Result()
.setCode(ResultCodeEnum.SUCCESS)
.setMessage(DEFAULT_SUCCESS_MESSAGE)
.setData(data);
}
public static Result genFailResult(String message) {
return new Result()
.setCode(ResultCodeEnum.FAIL)
.setMessage(message);
}
public static Result genUnauthorizedResult() {
return new Result()
.setCode(ResultCodeEnum.UNAUTHORIZED)
.setMessage("許可權不足!");
}
}
Hello World
新建IndexController
package top.zhaodongxx.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import top.zhaodongxx.result.Result;
import top.zhaodongxx.result.ResultGenerator;
/**
* <P></P>
*
* @author zhaodong
* @version v1.0
* @email [email protected]
* @since 2018/3/30 21:55
*/
@RestController
public class IndexController {
@GetMapping("/helloworld")
public Result helloWorld() {
return ResultGenerator.genSuccessResult("helloworld");
}
}
執行SpringBoot2ShiroProjectApplication.java
即可啟動該Spring Boot專案。
訪問路徑127.0.0.1:8080/helloworld
專案執行成功!
整合 Shiro
在Spring Boot2專案中整合Shiro主要分為四步
- 匯入Shiro
- 建立Shiro配置檔案,並在其中配置
DefaultWebSecurityManager
和ShiroFilterFactoryBean
- 實現身份認證的具體邏輯Realm
- 實現登入介面
匯入 Shiro
Shiro提供了一個啟動器來完成與 Spring Boot 的整合。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.4.0</version>
</dependency>
配置 Shiro
因為本節只是介紹身份認證,所以只提供一個最簡單的 Java Class 配置
package top.zhaodongxx.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
/**
* <P></P>
*
* @author zhaodong
* @version v1.0
* @email [email protected]
* @since 2018/3/30 22:41
*/
@Configuration
public class ShiroConfig {
/**
* 自定義的Realm
*/
@Bean(name = "myShiroRealm")
public MyShiroRealm myShiroRealm(){
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
設定realm.
securityManager.setRealm(myShiroRealm());
return securityManager;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
}
安全管理器DefaultWebSecurityManager
是Shiro的核心模組,ShiroFilterFactoryBean
用來配置需要被攔截的請求,被攔截下來的請求交給安全管理器管理。myShrioRealm
在idea中現在還是紅色的,因為我們還沒有寫。
自定義Shiro
新建一個服務,用來模擬從資料庫中取出使用者資訊,
package top.zhaodongxx.service;
import org.springframework.stereotype.Service;
/**
* @author zhaodong
* @version v1.0
* @email [email protected]
* @since 2018/3/30 22:59
*/
@Service
public class ShiroService {
public String getPasswordByUsername(String username){
switch (username){
case "liming":
return "123";
case "hanli":
return "456";
default:
return null;
}
}
}
Realm 是控制認證和授權的核心部分,也是開發人員必須自己實現的部分。
自定義的Realm通過繼承AuthorizingRealm
類,實現它的兩個方法:doGetAuthorizationInfo
和doGetAuthenticationInfo
實現自己的認證和授權邏輯,其中doGetAuthenticationInfo
處理授權邏輯暫時不實現。
package top.zhaodongxx.shiro;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 top.zhaodongxx.service.ShiroService;
import javax.annotation.Resource;
/**
*
* @author zhaodong
* @version v1.0
* @email [email protected]
* @since 2018/3/30 22:55
*/
public class MyShiroRealm extends AuthorizingRealm {
@Resource
public ShiroService shiroService;
/**
* 授權
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
return authorizationInfo;
}
/**
* 登入認證
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//獲取使用者賬號
String username = token.getPrincipal().toString();
String password = shiroService.getPasswordByUsername(username);
if (password != null) {
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
username, //認證通過後,存放在session,一般存放user物件
password, //使用者資料庫中的密碼
getName()); //返回Realm名
return authenticationInfo;
}
return null;
}
}
實現登入介面
AuthenticationException
是Shiro封裝的異常,如果登入認證沒有成功就會丟擲這個異常。
package top.zhaodongxx.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import top.zhaodongxx.result.Result;
import top.zhaodongxx.result.ResultGenerator;
/**
*
* @author zhaodong
* @version v1.0
* @email [email protected]
* @since 2018/3/30 23:05
*/
@RestController
public class LoginController {
@PostMapping("/doLogin")
public Result doLogin(String username, String password) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
} catch (AuthenticationException e) {
token.clear();
return ResultGenerator.genFailResult("登入失敗,使用者名稱或密碼錯誤!");
}
return ResultGenerator.genSuccessResult("登入成功");
}
}
測試
重新啟動專案。
通過postman
訪問127.0.0.1:8080/doLogin?username=liming&password=1231
,
登陸失敗
訪問127.0.0.1:8080/doLogin?username=liming&password=123
,
登入成功
至此,通過Shiro實現了身份認證。
專案下載地址
- github: spring-boot2-shiro-project