shiro是一款权限控制的后台框架,可以控制用户-角色-权限的分配,例如资源和路径等等。
主要内容:
ShiroFilterFactoryBean工厂类
SecurityManager安全管理器
MyRealm extends AuthorizingRealm自定义Realm,实现doGetAuthenticationInfo(认证)和doGetAuthorizationInfo(授权)方法。
还有其他一些rememberMe、Session和Cache功能。
下面使用SpringBoot整合Shiro的例子简单说明
表:数据中用户密码要存储加密后的密文
pom.xml文件
<?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>
<groupId>online.myson</groupId>
<artifactId>shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Shiro</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
shiro配置类
package online.myson.config;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import online.myson.shiro.MyRealm;
@Configuration
public class ShiroConfig {
/**
* 注册ShiroFilterFactoryBean,其实是注册一个filter
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 设置拦截器
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
// 过滤链定义,从上向下执行,一般将/**放最下面
// authc:所有的url都必须认证通过才可以访问;anon:所有url都可以匿名访问
filterChainDefinitionMap.put("/statics/**", "anon");
filterChainDefinitionMap.put("/login.html", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/user/**", "user");
filterChainDefinitionMap.put("/*", "authc");
// 不设置自动寻找根目录下的/login.jsp页面
shiroFilterFactoryBean.setLoginUrl("/index");
// 登录成功后要跳转的连接
shiroFilterFactoryBean.setSuccessUrl("/index");
// 未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 注册securityManager
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
securityManager.setRememberMeManager(cookieRememberMeManager());
return securityManager;
}
/**
* 注册自定义Realm
*/
@Bean
public MyRealm myRealm() {
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myRealm;
}
/**
* 注册HashedCredentialsMatcher,加解密
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
/**
* 注册CookieRememberMeManager
*/
@Bean
public CookieRememberMeManager cookieRememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(simpleCookie());
return cookieRememberMeManager;
}
/**
* 注册SimpleCookie
*/
@Bean
public SimpleCookie simpleCookie() {
SimpleCookie simpleCookie = new SimpleCookie();
simpleCookie.setPath("/");
simpleCookie.setHttpOnly(true);
simpleCookie.setMaxAge(604800);// 时间
return simpleCookie;
}
/**
* 开启shiro aop支持
* 用于授权
*/
@Bean
public AuthorizationAttributeSourceAdvisor AuthorizationAttributeSourceAdvisor(
SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
public static void main(String[] args) {
Object obj = new SimpleHash("md5","123456",ByteSource.Util.bytes("user"),2);
System.out.println(obj);
}
}
1、认证
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取用户输入的账号
String username = (String)token.getPrincipal();
System.out.println(token.getCredentials());
// 查询是否存在账号
SysUser user = sysUserService.findByUsername(username);
System.out.println(user);
if(user == null) {
return null;
}
// 验证密码
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user,
user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),// salt
"MyRealm"// realmName
);
return authenticationInfo;
}
Controller类:
@PostMapping("/login")
public ModelAndView login(SysUser user) {
ModelAndView mv = new ModelAndView("/login");
String msg = "成功";
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token =
new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
}catch(Exception e) {
if(e instanceof UnknownAccountException) {
msg = "账户不存在";
}else if(e instanceof IncorrectCredentialsException) {
msg = "密码不正确";
}else {
msg = e.getMessage();
}
}
mv.addObject("msg", msg);
return mv;
}
2、授权
三种方式:
a、通过Subject.isPermitted("userInfo:view")和subject.hasRole("admin")的一些方法,if else来控制
b、通过注解AOP控制
c、jsp页面使用标签<shiro>控制
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
SysUser user = (SysUser) principals.getPrimaryPrincipal();
// 获取用户的角色和对应的权限,这里一次性查角色和权限了,也可以写sql查
for(SysRole role : user.getRoleList()) {
authorizationInfo.addRole(role.getRole());
for(SysPermission permission : role.getPermissions()) {
authorizationInfo.addStringPermission(permission.getPermission());
}
}
return authorizationInfo;
}
Controller:
/**
* 用户添加
*/
@GetMapping("/user/add")
@RequiresPermissions("userInfo:view")
public String userAdd() {
Subject subject = SecurityUtils.getSubject();
// if(subject.isPermitted("userInfo:view")) {
// return "userAdd";
// }else {
// return "no authc";
// }
return "userAdd";