上一篇简单学习了一下shiro。这篇我们整合springboot和shiro
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shiro</groupId>
<artifactId>shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>springFreemarker-1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- shiro相关jar -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
shiro配置类
/**
*
*/
package shiro.config;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import shiro.realm.MyRealm;
/**
* @author JXQ
* shiro配置类
*/
@Configuration
public class ShiroConfig {
/**
* 核心的安全事务管理器
* @return
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
return securityManager;
}
/**
* 注入自定义的realm
* @return
*/
@Bean
public MyRealm myRealm() {
MyRealm realm = new MyRealm();
realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//默认跳转到登陆页面
//shiroFilterFactoryBean.setLoginUrl("/login");
//登陆成功后的页面
//shiroFilterFactoryBean.setSuccessUrl("/index");
//shiroFilterFactoryBean.setUnauthorizedUrl("/403");
Map<String,Filter> filterMap = new LinkedHashMap<>();
shiroFilterFactoryBean.setFilters(filterMap);
//权限控制map
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 哈希密码比较器,在realm中作用参数使用
* 登陆时会比较用户输入的密码,跟数据库密码配合盐值salt解密后是否一致。
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher macher = new HashedCredentialsMatcher();
macher.setHashAlgorithmName("md5");
macher.setHashIterations(1);
return macher;
}
/**
* 开启shiro aop注解支持
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor advisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* Shiro生命周期处理器
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
* 自动创建代理
* @return
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
}
自定义的Realm类:
/**
*
*/
package shiro.realm;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
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.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
/**
* @author JXQ
*
*/
public class MyRealm extends AuthorizingRealm{
Map<String, String> map = new HashMap<String, String>();
{
map.put("admin", "123456");
super.setName("MyRealm");//这个名字可以随便取
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//从主体传过来的认证信息中获取用户名
String userName = (String)principals.getPrimaryPrincipal();
//模拟从数据库获取角色和权限
Set<String> roleSet = getRoleByUserName(userName);
Set<String> permissionSet = getPermissinByUserName(userName);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRoles(roleSet);
authorizationInfo.addStringPermissions(permissionSet);
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//从主体传过来的认证信息中获取用户名
String userName = (String)token.getPrincipal();
//根据用户名获取密码,模拟从数据库获取
//String password = getPassWord(userName);
/*
* if(password == null) { return null; }
*/
Md5Hash password = new Md5Hash("123456", "TEST");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, password,ByteSource.Util.bytes("TEST") ,"MyRealm");
return simpleAuthenticationInfo;
}
private String getPassWord(String userName) {
String password = map.get(userName);
return password;
}
/**
* 从数据库获取角色
* @param userName
* @return
*/
private Set<String> getRoleByUserName(String userName) {
Set<String> set = new HashSet<String>();
set.add("admin");
set.add("aaaaa");
return set;
}
private Set<String> getPermissinByUserName(String userName) {
Set<String> set = new HashSet<String>();
set.add("user:delete");
set.add("user:update");
return set;
}
}
Controller类:
/**
*
*/
package shiro.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author JXQ
*
*/
@RestController
@RequestMapping(value = "test")
public class TestController {
@RequestMapping(value = "admin")
@RequiresPermissions("user:delete")
public String admin() {
return "测试成功";
}
@RequestMapping(value = "login")
public String login(String userName, String password) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
subject.login(token);
return "登录成功";
}
}
在使用postman测试之前一定要先调用login。不然会出错。我们在set中设置了用户拥有user:delete权限和user:update权限
而访问admin方法需要user:delete权限。发现能够正确的访问。
修改访问admin方法需要的权限为:user:view
会发现报错信息为:
Not authorized to invoke method: public java.lang.String shiro.controller.TestController.admin()
其实这是没有配置没有权限时的访问路径。
shiro的自定义过滤器
package shiro.filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
public class ShiroFilter extends AuthorizationFilter{
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object o)
throws Exception {
Subject subject = getSubject(request, response);
String[] roles = (String[])o;//需要的权限
if(null == roles || roles.length == 0) {
//说明不需要任何权限就可以访问
return true;
}
for(String role : roles) {
if(subject.hasRole(role)) {
return true;
}
}
return false;
}
}
修改shiroConfiguration文件:
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//默认跳转到登陆页面
//shiroFilterFactoryBean.setLoginUrl("/login");
//登陆成功后的页面
//shiroFilterFactoryBean.setSuccessUrl("/index");
//shiroFilterFactoryBean.setUnauthorizedUrl("/index.ftl");
Map<String,Filter> filterMap = new LinkedHashMap<>();
filterMap.put("url", new ShiroFilter());
shiroFilterFactoryBean.setFilters(filterMap);
//权限控制map
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/test/admin", "url[admin,admin1]");
//filterChainDefinitionMap.put("/test/admin", "url");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
这个过滤器的意思是:当我们的admin方法访问需要的角色为admin或者admin1时,都可以访问这个方法。