1、SSM整合shiro
1.1 添加依賴(lài)
和之前的SSM相比速缨,整合shiro的時(shí)候需要添加一個(gè)依賴(lài)
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
shiro-spring這個(gè)依賴(lài)包含了shiro-core與shiro-web尤慰,添加之后就不再需要添加shiro-core和shiro-web了关斜。
1.2 spring-shiro.xml配置
spring-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置shiro的過(guò)濾器工廠-->
<bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" id="shiroFilter">
<!--設(shè)置securityManager-->
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/index.jsp"/>
<!-- override these for application-specific URLs if you like:
<property name="loginUrl" value="/login.jsp"/>
<property name="successUrl" value="/home.jsp"/>
<property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
<!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean -->
<!-- defined will be automatically acquired and available via its beanName in chain -->
<!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
<!-- <property name="filters">
<util:map>
<entry key="anAlias" value-ref="someFilter"/>
</util:map>
</property> -->
<!--配置url與權(quán)限-->
<property name="filterChainDefinitions">
<value>
/index.jsp=anon
/main.jsp=authc
/manager.jsp=roles[manager]
/guest.jsp=roles[guest]
</value>
</property>
</bean>
<!--配置默認(rèn)的webSecurityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app. If you have multiple realms, use the 'realms' property instead. -->
<!--自定義自己的realm-->
<property name="realm" ref="myRealm"/>
<!-- By default the servlet container sessions will be used. Uncomment this line
to use shiro's native sessions (see the JavaDoc for more): -->
<!-- <property name="sessionMode" value="native"/> -->
</bean>
<bean id="myRealm" class="com.qianfeng.shiro.MyRealm"/>
<bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor" id="lifecycleBeanPostProcessor"/>
</beans>
這個(gè)xml文件配置了四個(gè)bean:ShiroFilterFactoryBean叽粹、DefaultWebSecurityManager炭菌、MyRealm跃须、LifecycleBeanPostProcessor。ShiroFilterFactoryBean的作用是配置過(guò)濾器工廠娃兽,id要和稍后要說(shuō)的web.xml中的shiro過(guò)濾器的名稱(chēng)一樣,指定securityManager尽楔,等于頁(yè)面投储,過(guò)濾頁(yè)面。DefaultWebSecurityManager這個(gè)的主要作用就是配置realm阔馋,如果有多個(gè)realm就要配置realms玛荞,MyRealm是我們自定義的Realm,然后LifecycleBeanPostProcessor是一個(gè)AOP的實(shí)現(xiàn)呕寝。
1.3 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置過(guò)濾器勋眯,這個(gè)過(guò)濾器的名字應(yīng)該和spring-shiro.xml中過(guò)濾器的id一樣-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!--
將過(guò)濾器的生命周期從出生到死亡完全交給Spring來(lái)管理
-->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
<!-- requests. Usually this filter mapping is defined first (before all others) to -->
<!-- ensure that Shiro works in subsequent filters in the filter chain: -->
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- spring寫(xiě)好的中文過(guò)濾器-->
<filter>
<filter-name>encode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
和shiro相關(guān)的就是一個(gè)過(guò)濾器和一個(gè)監(jiān)聽(tīng)器,這個(gè)過(guò)濾器類(lèi)是DelegatingFilterProxy下梢,name應(yīng)該和spring-shiro.xml中ShiroFilterFactoryBean的id一樣客蹋,監(jiān)聽(tīng)器是ContextLoaderListener,文本加載監(jiān)聽(tīng)器孽江,context-param這個(gè)標(biāo)簽將classpath目錄下的所有xml文件讀取進(jìn)來(lái)讶坯。
1.4 MyRealm
MyReanl.java
package com.qianfeng.shiro;
import com.qianfeng.entity.Employee;
import com.qianfeng.entity.Permission;
import com.qianfeng.entity.Roles;
import com.qianfeng.service.IEmployeeService;
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 javax.annotation.Resource;
import java.util.List;
/**
* @author huwen
*/
public class MyRealm extends AuthorizingRealm {
@Resource
private IEmployeeService employeeService;
/**
* 授權(quán)方法
* @param principalCollection principal的集合,可以理解為各種用戶(hù)身份的集合岗屏,比如用戶(hù)名辆琅、郵箱、手機(jī)號(hào)等
* @return 返回的是授權(quán)信息这刷,包括角色與權(quán)限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//SimpleAuthorizationInfo是AuthorizationInfo的子類(lèi)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//得到可用的principal婉烟,然后轉(zhuǎn)換為字符串
String empName = getAvailablePrincipal(principalCollection).toString();
//調(diào)用service
List<Roles> roles = employeeService.getAllRolesByEmpName(empName);
for (Roles role : roles) {
info.addRole(role.getRoleName());
}
List<Permission> permissions = employeeService.getAllPermissionsByEmpName(empName);
for (Permission permission : permissions) {
info.addStringPermission(permission.getPermName());
}
return info;
}
/**
* 這個(gè)方法用于認(rèn)證
* @param authenticationToken 用戶(hù)名與密碼
* @return 認(rèn)證信息
* @throws AuthenticationException 可能引發(fā)的異常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
AuthenticationInfo info = null;
//將authenticationToken強(qiáng)轉(zhuǎn)為usernamePasswordToken,向下轉(zhuǎn)型能夠成功因?yàn)槲覀冎烙脩?hù)輸入的是用戶(hù)名與密碼
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//從token中獲取用戶(hù)名與密碼傳給service暇屋,最后交給dao從數(shù)據(jù)庫(kù)中查詢(xún)
String username = token.getUsername();
//使用字節(jié)數(shù)組存儲(chǔ)密碼更為安全似袁,因?yàn)樽止?jié)數(shù)組是可變的,字符串是不可變的存在常量池中
char[] password = token.getPassword();
String pass = new String(password);
Employee emp = employeeService.getEmployeeByNameAndPassword(username,pass);
//如果查詢(xún)到的數(shù)據(jù)不為空率碾,就構(gòu)造一個(gè)SimpleAuthenticationInfo對(duì)象叔营,將用戶(hù)名與密碼放在里里面
if(emp!=null && emp.getEmpId()!=0){
//getName()獲取到的是當(dāng)前Realm的標(biāo)識(shí),因?yàn)榭梢宰远x多個(gè)Realm所宰,不同的Realm需要區(qū)分
info = new SimpleAuthenticationInfo(username,pass,getName());
}
return info;
}
}
MyRealm繼承自AuthorizingRealm绒尊,重寫(xiě)兩個(gè)方法,第一個(gè)方法用于授權(quán)仔粥,第二個(gè)方法用于認(rèn)證婴谱。
1.5 EmployeeController
package com.qianfeng.controller;
import com.qianfeng.entity.Employee;
import com.qianfeng.service.IEmployeeService;
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.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import javax.annotation.Resource;
@Controller
public class EmployeeController {
@Resource
private IEmployeeService employeeService;
@PostMapping("/login")
public String empLogin(Employee employee){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(employee.getEmpName(),employee.getPassword());
try {
subject.login(token);
return "main";
} catch (AuthenticationException e) {
e.printStackTrace();
}
return "index";
}
}
可以看到和之前的web項(xiàng)目的不同蟹但,不需要我們手動(dòng)創(chuàng)建配置工廠生成實(shí)例了。
2谭羔、SpringBoot整合shiro
2.1 添加依賴(lài)
這里shiro需要的依賴(lài)和SSM項(xiàng)目中的依賴(lài)一樣华糖,都是shiro-spring。如果想要在thymeleaf中使用shiro標(biāo)簽瘟裸,需要添加下面的依賴(lài):
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
然后載html標(biāo)簽中添加xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"客叉。
2.2 MyRealm
package com.qianfeng.shiro;
import com.qianfeng.entity.Employee;
import com.qianfeng.entity.Permission;
import com.qianfeng.entity.Roles;
import com.qianfeng.service.IEmployeeService;
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.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* @author huwen
*/
@Component("myRealm")
public class MyRealm extends AuthorizingRealm {
@Resource
private IEmployeeService employeeService;
/**
* 授權(quán)方法
* @param principalCollection principal的集合,可以理解為各種用戶(hù)身份的集合话告,比如用戶(hù)名兼搏、郵箱、手機(jī)號(hào)等
* @return 返回的是授權(quán)信息沙郭,包括角色與權(quán)限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//SimpleAuthorizationInfo是AuthorizationInfo的子類(lèi)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//得到可用的principal佛呻,然后轉(zhuǎn)換為字符串
String empName = getAvailablePrincipal(principalCollection).toString();
//調(diào)用service
List<Roles> roles = employeeService.getAllRolesByEmpName(empName);
for (Roles role : roles) {
info.addRole(role.getRoleName());
}
List<Permission> permissions = employeeService.getAllPermissionsByEmpName(empName);
for (Permission permission : permissions) {
info.addStringPermission(permission.getPermName());
}
return info;
}
/**
* 這個(gè)方法用于認(rèn)證
* @param authenticationToken 用戶(hù)名與密碼
* @return 認(rèn)證信息
* @throws AuthenticationException 可能引發(fā)的異常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
AuthenticationInfo info = null;
//將authenticationToken強(qiáng)轉(zhuǎn)為usernamePasswordToken,向下轉(zhuǎn)型能夠成功因?yàn)槲覀冎烙脩?hù)輸入的是用戶(hù)名與密碼
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//從token中獲取用戶(hù)名與密碼傳給service病线,最后交給dao從數(shù)據(jù)庫(kù)中查詢(xún)
String username = token.getUsername();
//使用字節(jié)數(shù)組存儲(chǔ)密碼更為安全吓著,因?yàn)樽止?jié)數(shù)組是可變的,字符串是不可變的存在常量池中
char[] password = token.getPassword();
String pass = new String(password);
Employee emp = employeeService.getEmployeeByNameAndPassword(username,pass);
//如果查詢(xún)到的數(shù)據(jù)不為空送挑,就構(gòu)造一個(gè)SimpleAuthenticationInfo對(duì)象绑莺,將用戶(hù)名與密碼放在里里面
if(emp!=null && emp.getEmpId()!=0){
//getName()獲取到的是當(dāng)前Realm的標(biāo)識(shí),因?yàn)榭梢宰远x多個(gè)Realm惕耕,不同的Realm需要區(qū)分
info = new SimpleAuthenticationInfo(username,pass,getName());
}
return info;
}
}
MyRealm這個(gè)類(lèi)和SSM項(xiàng)目基本一樣紊撕,唯一的不同是加上了@Component("myRealm")這個(gè)注解
2.3 ShiroConfig
ShiroConfig.java
package com.qianfeng.config;
import com.qianfeng.shiro.MyRealm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
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 {
/**
* 該方法用于返回過(guò)濾器工廠
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,String> map = new HashMap<>(10);
map.put("/index.html","anon");
map.put("/main.html","authc");
shiroFilterFactoryBean.setLoginUrl("/login.html");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth.html");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
/**
* 設(shè)置realm為自定義的Realm,返回securityManager
* @param realm
* @return
*/
@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("myRealm")MyRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
/**
* 開(kāi)啟shiro的注解赡突,需要借助SpringAOP掃描Shiro注解的類(lèi)对扶,來(lái)進(jìn)行安全校驗(yàn)
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 開(kāi)啟aop的注解支持
* @param defaultWebSecurityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
return authorizationAttributeSourceAdvisor;
}
}
由于springboot不使用xml文件進(jìn)行配置,所以采用Java類(lèi)的方式對(duì)shiro進(jìn)行設(shè)置惭缰,整個(gè)類(lèi)上有一個(gè)注解@Configuration浪南,共有四個(gè)方法每個(gè)方法都有一個(gè)@Bean注解,方法名可以自定義漱受,但是參數(shù)與返回值必須對(duì)應(yīng)络凿,第一個(gè)方法的作用是設(shè)置并返回過(guò)濾器工廠,第二個(gè)方法的作用是設(shè)置securityManager并返回昂羡,下面那兩個(gè)方法都是和AOP相關(guān)的絮记。