Shiro
核心组件
- UsernamePasswordToken: shiro用来封装用户登录信息,使用用户的登录信息来创建令牌Token
- SecurityManager :shiro的核心部分,负责安全认证和授权
- Subject :Shiro的一个抽象概念,包含了用户信息
- Realm :开发者自定义的模块,验证和授权的逻辑全部写在Realm中
- AuthenticationInfo:用户的角色信息集合,认证时使用
- AuthorzationInfo:用户的权限信息集合,授权时使用
- DefaultWebSecurityDManager:安全管理器,开发者自定义Realm需要注入到进行管理才能生效
- ShiroFilterFactoryBean:过滤器工厂
功能简介
Authentication :身份认证/登录
Authorization :授权,权限验证
SessionManagement:会话管理
Cryptography:加密
WebSupport : Web支持
Caching: 缓存
Concurrency: 并发验证
Testing:测试
“Run As”:允许一个用户假装另一个用户
Remember me:记住我
Spring Boot 整合 shiro
认证过滤器
anon:无需认证
autc:必须认证
authBasic:需要通过HttpBasic认证
user:不一定通过认证,只要曾经被shiro记录即可,(记住我)
授权过滤器
perms:必须拥有某个权限
roles:必须拥有某个角色
prot:必须 指定端口
rest:请求必须基于 RestFul
ssl:必须是安全的url请求 ,协议HTTPS
shiro依赖
<!--springBoot 整合Shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
<!--shiro-ehCache依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<!--Redis缓存依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
shiro配置类
@Configuration
public class MyShiRo {
/**
* 将自定义的Realm组件交给Bean容器
* @return
*/
@Bean
public UserRealm userRealm(){
//创建自定义Realm对象
UserRealm userRealm = new UserRealm();
// 匹配器 对象
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
// 加密方式名
credentialsMatcher.setHashAlgorithmName("MD5");
// 加密次数
credentialsMatcher.setHashIterations(1024);
// 将匹配器 存入对象中
userRealm.setCredentialsMatcher(credentialsMatcher);
// 设置 缓存管理对象
userRealm.setCacheManager(new RedisCacheManager());
// 开启 角色认证全局缓存
userRealm.setAuthenticationCachingEnabled(true);
// 设置 角色认证 缓存名
userRealm.setAuthenticationCacheName("authentication");
// 开启 权限授权全局缓存
userRealm.setAuthorizationCachingEnabled(true);
// 设置 权限授权 缓存名
userRealm.setAuthorizationCacheName("authorization");
return userRealm;
}
/**
* 将DefaultWebSecurityManager组件 交给Bean容器
* @param userRealm
* @return
*/
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
manager.setRealm(userRealm);
return manager;
}
/**
* 创建ShiroFilter工厂 交给Bean容器
* @param defaultWebSecurityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
//创建ShiroFilter工厂对象
ShiroFilterFactoryBean factoryBean= new ShiroFilterFactoryBean();
//将安全管理加到工厂对象
factoryBean.setSecurityManager(defaultWebSecurityManager);
//设置 认证,权限集
Map<String,String> map=new HashMap<String, String>();
map.put("/index","authc");
map.put("/","authc");
map.put("/vip","perms[manage]");
map.put("/admin","roles[admin]");
map.put("/login","anon");
map.put("/register","anon");
//将权限集 放入到 工厂对象中
factoryBean.setFilterChainDefinitionMap(map);
//设置 登录页面
factoryBean.setLoginUrl("/login");
//设置 无授权页面
factoryBean.setUnauthorizedUrl("/index");
return factoryBean;
}
}
shiro-自定义realm
public class UserRealm extends AuthorizingRealm {
/**
* 权限授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取subject对象
Subject subject= SecurityUtils.getSubject();
//获取subject对象里的 object对象
User user = (User) subject.getPrincipal();
//Set集合 放权限名
Set<String> set = new HashSet<>();
set.add(user.getRole());
//将权限集放进SimpleAuthorizationInfo对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(set);
//将权限 与角色相连
info.addStringPermission(user.getPerms());
return info;
}
/**
* 角色认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//封装前台用户信息
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
UserServiceImpl userService = (UserServiceImpl) SpringUtils.getBean("userServiceImpl");
//前台传的用户名在数据库里查找
User user = userService.findUserByName(token.getUsername());
if(user !=null){
//返回一个AuthenticationInfo对象
return new SimpleAuthenticationInfo(user,user.getPassword(), new MyByteSource(user.getSalt()),this.getName());
}
return null;
}
}
controller层
@PostMapping("/login")
public String loginAuthentication(String username, String password, Model model){
// subject对象
Subject subject= SecurityUtils.getSubject();
//封装用户信息
UsernamePasswordToken token=new UsernamePasswordToken(username,password);
try{
subject.login(token);
return "redirect:/";
}catch (UnknownAccountException e){
e.printStackTrace();
model.addAttribute("msg","账户错误");
return "login";
}catch (IncorrectCredentialsException e){
e.printStackTrace();
model.addAttribute("msg","密码错误");
return "login";
}
}
加密(MD5)
在shiro配置类 userRealm中加上
// 匹配器 对象
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
// 加密方式名
credentialsMatcher.setHashAlgorithmName("MD5");
// 加密次数
credentialsMatcher.setHashIterations(1024);
// 将匹配器 存入对象中
userRealm.setCredentialsMatcher(credentialsMatcher);
加密方式 Service层
public void register(User user) {
//获取八位数的盐
String salt= SaltUtils.getSalt(8);
//将盐保存到数据里
user.setSalt(salt);
//明文密码进行md5+salt+hash散列
Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024);
//将md5密码赋值给数据里的password
user.setPassword(md5Hash.toHex());
userMapper.insertUser(user);
}
salt工具类
public class SaltUtils {
public static String getSalt(int n){
char[] chars = "QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuioplkjhgfdsazxcvbnm1234567890!@#$%^&*".toCharArray();
StringBuilder sb= new StringBuilder();
for (int i = 0; i < n; i++) {
char achar=chars[new Random().nextInt(chars.length)];
sb.append(achar);
}
return sb.toString();
}
}
缓存(EhCache,RedisCache)
依赖
<!--shiro-ehCache依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
在shiro配置类 自定义userRealm
// 设置 缓存管理对象
userRealm.setCacheManager(new EhCacheManager());
// 开启 角色认证全局缓存
userRealm.setAuthenticationCachingEnabled(true);
// 设置 角色认证 缓存名
userRealm.setAuthenticationCacheName("authentication");
// 开启 权限授权全局缓存
userRealm.setAuthorizationCachingEnabled(true);
// 设置 权限授权 缓存名
userRealm.setAuthorizationCacheName("authorization");
Redis缓存
- 实现shiro缓存底层的cacheManager 和cache 接口
实现cache
/**
* 实现Shiro中的Cache
* @author yd
* @version 1.0
* @date 2020/10/4 8:46
*/
public class MyRedis<K,V> implements Cache<K,V> {
public String cacheName;
public MyRedis() {
}
public MyRedis(String cacheName) {
this.cacheName = cacheName;
}
public RedisTemplate redisTemplate(){
RedisTemplate redisTemplate = (RedisTemplate) SpringUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
@Override
public V get(K k) throws CacheException {
return (V) redisTemplate().opsForHash().get(this.cacheName,k.toString());
}
@Override
public V put(K k, V v) throws CacheException {
redisTemplate().opsForHash().put(this.cacheName,k.toString(),v);
return null;
}
@Override
public V remove(K k) throws CacheException {
return (V) redisTemplate().opsForHash().delete(this.cacheName,k.toString());
}
@Override
public void clear() throws CacheException {
redisTemplate().delete(this.cacheName);
}
@Override
public int size() {
return redisTemplate().opsForHash().size(this.cacheName).intValue();
}
@Override
public Set<K> keys() {
return redisTemplate().opsForHash().keys(this.cacheName);
}
@Override
public Collection<V> values() {
return redisTemplate().opsForHash().values(this.cacheName);
}
}
实现CacheManager
/**
* 实现Shiro的CacheManager
* @author yd
* @version 1.0
* @date 2020/10/4 8:43
*/
public class RedisCacheManager implements CacheManager {
@Override
public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
return new MyRedis<K,V>(cacheName);
}
}