Springboot+Shiro+redis整合

1、Shiro是Apache下的一個開源項目,我們稱之為Apache Shiro。它是一個很易用與Java項目的的安全框架,提供了認證、授權、加密、會話管理,與spring Security 一樣都是做一個權限的安全框架,但是與Spring Security 相比,在於 Shiro 使用了比較簡單易懂易於使用的授權方式。shiro屬於輕量級框架,相對於security簡單的多,也沒有security那麼複雜。所以我這裡也是簡單介紹一下shiro的使用。

2、非常簡單;其基本功能點如下圖所示:

Springboot+Shiro+redis整合

Springboot+Shiro+redis整合

Authentication:身份認證/登錄,驗證用戶是不是擁有相應的身份;

Authorization:授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能做事情,常見的如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個權限;

Session Manager:會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通JavaSE環境的,也可以是如Web環境的;

Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;

Web Support:Web支持,可以非常容易的集成到Web環境;

Caching:緩存,比如用戶登錄後,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;

Concurrency:shiro支持多線程應用的併發驗證,即如在一個線程中開啟另一個線程,能把權限自動傳播過去;

Testing:提供測試支持;

Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問;

Remember Me:記住我,這個是非常常見的功能,即一次登錄後,下次再來的話不用登錄了。

記住一點,Shiro不會去維護用戶、維護權限;這些需要我們自己去設計/提供;然後通過相應的接口注入給Shiro即可。

3、這裡我就簡單介紹一下springboot和shiro整合與基本使用。

需要的基礎包:pom.xml

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.winter

HtmlCssJs

0.0.1-SNAPSHOT

war

HtmlCssJs

This is Html Css Js Demo Project

org.springframework.boot

spring-boot-starter-parent

1.5.3.RELEASE

UTF-8

UTF-8

1.8

net.sourceforge.nekohtml

nekohtml

net.sf.json-lib

json-lib

2.4

jdk15

org.apache.shiro

shiro-spring

1.4.0

org.quartz-scheduler

quartz

2.2.1

org.springframework

spring-context-support

org.mybatis.spring.boot

mybatis-spring-boot-starter

1.3.0

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-starter-aop

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-tomcat

org.apache.tomcat

tomcat-servlet-api

8.0.36

provided

org.springframework.boot

spring-boot-starter-data-jpa

org.springframework.boot

spring-boot-starter-test

test

org.springframework.boot

spring-boot-starter-mail

mysql

mysql-connector-java

javax.servlet

javax.servlet-api

javax.servlet

jstl

org.apache.tomcat

tomcat-jsp-api

org.apache.tomcat.embed

tomcat-embed-core

org.apache.tomcat.embed

tomcat-embed-jasper

com.fasterxml.jackson.core

jackson-core

com.fasterxml.jackson.core

jackson-databind

com.fasterxml.jackson.datatype

jackson-datatype-joda

com.fasterxml.jackson.module

jackson-module-parameter-names

com.github.pagehelper

pagehelper-spring-boot-starter

1.1.2

com.alibaba

druid-spring-boot-starter

1.1.0

redis.clients

jedis

org.springframework.boot

spring-boot-starter-data-redis

org.springframework.data

spring-data-redis

aspectj

aspectjweaver

1.5.3

org.weixin4j.spring.boot

weixin4j-spring-boot-starter

1.0.0

com.soecode.wx-tools

wx-tools

2.1.4-RELEASE

org.springframework.boot

spring-boot-maven-plugin

基本配置application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/world?useUnicode=true&characterEncoding=utf8

spring.datasource.username=root

spring.datasource.password=root

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

spring.datasource.initialSize=5

spring.datasource.minIdle=5

spring.datasource.maxActive=20

spring.datasource.maxWait=60000

mybatis.mapper-locations=classpath*:mapper/*Mapper.xml

mybatis.type-aliases-package=com.cwh.springbootMybatis.entity

#thymeleaf模板使用

#關閉thymeleaf緩存

spring.thymeleaf.cache=false

#去掉thymeleaf的嚴格的模板校驗

#spring.thymeleaf.mode=LEGACYHTML5

#Spring Jpa

spring.jpa.properties.hibernate.hbm2ddl.auto=update

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

spring.jpa.show-sql= true

# Redis數據庫索引(默認為0)

spring.redis.database=0

# Redis服務器地址

spring.redis.host=127.0.0.1

# Redis服務器連接端口

spring.redis.port=6379

# Redis服務器連接密碼(默認為空)

spring.redis.password=

# 連接池最大連接數(使用負值表示沒有限制)

spring.redis.pool.max-active=8

# 連接池最大阻塞等待時間(使用負值表示沒有限制)

spring.redis.pool.max-wait=-1

# 連接池中的最大空閒連接

spring.redis.pool.max-idle=8

# 連接池中的最小空閒連接

spring.redis.pool.min-idle=0

# 連接超時時間(毫秒)

spring.redis.timeout=0

OK ,開始我們需要3個實體,用戶,角色和權限

// 實體類名

@Entity

// 映射數據庫表名

@Table(name="tb_user")

public class User implements Serializable{

/**

*/

private static final long serialVersionUID = 1L;

@Id

// 主鍵自增長

@GeneratedValue(strategy=GenerationType.AUTO)

public Long id;

// 列名

@Column

public String name;

@Column

public String password;

@OneToMany(cascade=CascadeType.ALL)

private List roles;

// 省略get set..

@Entity

@Table(name="tb_role")

public class Role {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

private Long id;

private String roleName;

@ManyToOne(fetch = FetchType.EAGER)

private User user;

@OneToMany(cascade = CascadeType.ALL)

private List permissions;

// 省略get set..

@Entity

@Table(name="tb_permission")

public class Permission {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

private Long id;

private String permission;

@ManyToOne(fetch = FetchType.EAGER)

private Role role;

// 省略get set..

重啟服務,對應看數據庫,已自動生成如下表:

Springboot+Shiro+redis整合

注入Shiro Factory和SecurityManager:

// shiro核心配置類

@Configuration

public class ShiroConfiguration {

@Bean

public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager){

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

// 必須設置 SecurityManager

shiroFilterFactoryBean.setSecurityManager(securityManager);

// setLoginUrl 如果不設置值,默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 或 "/login" 映射

shiroFilterFactoryBean.setLoginUrl("/nologin");

// 設置無權限時跳轉的 url;

shiroFilterFactoryBean.setUnauthorizedUrl("/403");

// 默認成功界面,登陸成功後 跳轉

shiroFilterFactoryBean.setSuccessUrl("/index");

// 設置攔截器

Map<string> filterChainDefinitionMap = new LinkedHashMap<>();/<string>

//遊客,開發權限

filterChainDefinitionMap.put("/guest/**", "anon");

//用戶,需要角色權限 “user”

filterChainDefinitionMap.put("/user/**", "roles[user]");

//管理員,需要角色權限 “admin”

filterChainDefinitionMap.put("/admin/**", "roles[admin]");

//開放登陸接口

filterChainDefinitionMap.put("/login", "anon");

//其餘接口一律攔截

//主要這行代碼必須放在所有權限設置的最後,不然會導致所有 url 都被攔截

filterChainDefinitionMap.put("/**", "authc");

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

System.out.println("Shiro攔截器工廠類注入成功");

return shiroFilterFactoryBean;

}

/**

* 注入 securityManager

*/

@Bean

public org.apache.shiro.mgt.SecurityManager securityManager() {

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

// 設置realm.

securityManager.setRealm(customRealm());

return securityManager;

}

/**

* 自定義身份認證 realm;

*

* 必須寫這個類,並加上 @Bean 註解,目的是注入 CustomRealm,

* 否則會影響 CustomRealm類 中其他類的依賴注入

*/

@Bean

public CustomRealm customRealm() {

return new CustomRealm();

}

}

注意:SecurityManager這有點坑,默認是導入java.lang.SecurityManager包 可以寫成我這種格式,即使不太美觀

org.apache.shiro.mgt.SecurityManager

這裡說下:ShiroFilterFactory中已經由Shiro官方實現的過濾器

Springboot+Shiro+redis整合

anon:所有url都都可以匿名訪問;

authc: 需要認證才能進行訪問;

user:配置記住我或認證通過可以訪問

身份認證

在認證、授權內部實現機制中都有提到,最終處理都將交給Real進行處理。因為在Shiro中,最終是通過Realm來獲取應用程序中的用戶、角色及權限信息的。通常情況下,在Realm中會直接從我們的數據源中獲取Shiro需要的驗證信息。可以說,Realm是專用於安全框架的DAO.

認證實現

public class CustomRealm extends AuthorizingRealm{

private UserRepository userMapper;

@Autowired

private void setUserMapper(UserRepository userMapper) {

this.userMapper = userMapper;

}

/**

* 獲取身份驗證信息

* Shiro中,最終是通過 Realm 來獲取應用程序中的用戶、角色及權限信息的。

*

* @param authenticationToken 用戶身份信息 token

* @return 返回封裝了用戶信息的 AuthenticationInfo 實例

*/

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

System.out.println("————身份認證方法————");

UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

// 從數據庫獲取對應用戶名密碼的用戶

User user = userMapper.findPasswordByName(token.getUsername());

if (null == user.getPassword()) {

throw new AccountException("用戶名不正確");

} else if (!user.getPassword().equals(new String((char[]) token.getCredentials()))) {

throw new AccountException("密碼不正確");

}

return new SimpleAuthenticationInfo(token.getPrincipal(), user.getPassword(), getName());

}

/**

*

* @param principalCollection

* @return

*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

System.out.println("————權限認證————");

String username = (String) SecurityUtils.getSubject().getPrincipal();

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

//獲得該用戶角色

User user = userMapper.findRoleByName(username);

Set set = new HashSet<>();

List roles = user.getRoles();

for (Role role2 : roles) {

String roleName = role2.getRoleName();

//需要將 role 封裝到 Set 作為 info.setRoles() 的參數

set.add(roleName);

}

//設置該用戶擁有的角色

info.setRoles(set);

return info;

}

}

Springboot+Shiro+redis整合

然後在控制器層;

@Controller

public class UserController {

@Autowired

private IUserService userService;

// 注:此處不能使用@ResponseBody 因為使用過後,控制層就當字符串返回了

// 創建Model對象,將信息以Key,Value形式放置Model域中

@RequestMapping("/findOne")

public String findOne(Long id,Model model){

User user = userService.findOne(id);

model.addAttribute("user", user);

return "/user/findOne";

}

@RequestMapping("/nologin")

public String nologin(){

return "/user/login";

}

/**

* 登陸

*

* @param username 用戶名

* @param password 密碼

*/

@RequestMapping(value = "/login", method = RequestMethod.POST)

@ResponseBody

public String login(String username, String password) {

// 從SecurityUtils裡邊創建一個 subject

Subject subject = SecurityUtils.getSubject();

// 在認證提交前準備 token(令牌)

UsernamePasswordToken token = new UsernamePasswordToken(username, password);

// 執行認證登陸

subject.login(token);

// 根據權限,指定返回數據

List role = userService.findRoleByName(username).getRoles();

for (Role role2 : role) {

if ("user".equals(role2.getRoleName())) {

return "歡迎登陸";

}

if ("admin".equals(role2.getRoleName())) {

return "歡迎來到管理員頁面";

}

}

return "權限錯誤!";

}

@RequestMapping("/index")

public String index(Model model){

model.addAttribute("msg", "歡迎訪問主頁");

return "/user/index";

}

}

省掉了登陸界面等

進行訪問,我們直接在地址欄訪問主頁。

192.168.40.88:8080/index會跳轉到登陸界面,因為我們還未登陸

Springboot+Shiro+redis整合

登陸後,

Springboot+Shiro+redis整合


分享到:


相關文章: