1.引用依赖包:(官方文档:shiro-redis/docs at master · alexxiyang/shiro-redis · GitHub)
<!-- shiro 依赖包,因为我需要把Session存储在redis,所以使用了这个依赖包 -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis-spring-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
2.配置application.yml文件
shiro-redis:
enabled: true
redis-manager:
host: 127.0.0.1:6379
3.编写shiro 相关配置,有5个文件,分别是MySessionManager(自定义shiro Session管理器)、MyRealm(自定义存储用户信息)、MyCredentialsMatcher(自定义密码验证器)、MyAuthenticationFilter(自定义拦截过滤器)、ShiroConfig(shiro配置类)。以下分别贴出各个文件的代码
MySessionManager:自定义shiro Session管理器
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
/*自定义shiro Session管理器*/
public class MySessionManager extends DefaultWebSessionManager {
private static final String Token_Header="test_id";
/*shiro 中的SessionId 通过header的test_id 获取 */
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response){
HttpServletRequest httpServletRequest= WebUtils.toHttp(request);
String sessionId= httpServletRequest.getHeader(Token_Header);
return sessionId;
}
}
MyRealm:自定义存储用户信息
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.beans.factory.annotation.Autowired;
import java.util.List;
//通过重写AuthorizingRealm,自定义存储用户的信息
public class MyRealm extends AuthorizingRealm {
@Autowired
Service service;
/*用户认证的方法*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
//查询数据库中的用户名信息
User user = service.getUserByUserName(username);
if (user == null) {
throw new UnknownAccountException("账号/密码错误");
}
String pwd =user.getF_password();
return new SimpleAuthenticationInfo(username, pwd, getName());
}
/*用户授权的方法*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//查询数据库中的用户名信息
UserDto user = service.getUserByUserName(username);
// 查询数据库获取用户角色信息,并添加到授权信息中
List<String> roles = user.getLstAccessRoles();
authorizationInfo.addRoles(roles);
// 查询数据库获取用户权限信息,并添加到授权信息中
List<String> permissions =user.getLstAccessApis();
authorizationInfo.addStringPermissions(permissions);
return authorizationInfo;
}
}
MyCredentialsMatcher:自定义密码验证器
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
/*密码验证器,需要解密后匹配*/
public class MyCredentialsMatcher extends SimpleCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info){
UsernamePasswordToken passwordToken=(UsernamePasswordToken) token;
String pwd=new String(passwordToken.getPassword());
String dbPwd=(String) info.getCredentials();
return pwd.equals(dbPwd);
}
}
MyAuthenticationFilter:自定义拦截过滤器
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class MyAuthenticationFilter extends FormAuthenticationFilter {
/*重新请求被拦截后的响应体,返回统一格式*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response){
HttpServletResponse httpServletResponse=(HttpServletResponse)response;
ServiceResult result=new ServiceResult();
result.isFailure("无操作权限");
try{
httpServletResponse.setContentType("application/json; charset=utf-8");
PrintWriter writer = httpServletResponse.getWriter();
String json = JSON.toJSONString(result, SerializerFeature.WriteMapNullValue);
writer.append(json);
writer.flush();
writer.close();
}
catch (IOException ex){
throw new RuntimeException(ex);
}
return false;
}
}
ShiroConfig:shiro配置类
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* 配置 shiro的过滤器
* 过滤器中配置 安全管理器
*
* */
@Configuration
public class ShiroConfig {
@Autowired
RedisSessionDAO redisSessionDAO;
@Autowired
RedisCacheManager redisCacheManager;
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager());
Map<String,String> map=new LinkedHashMap<>();
//所有请求/login的不拦截
map.put("/login/**","anon");//允许 需要设置login为anon
//拦截所有,要有顺序,先设置允许访问的,再设置拦截的
map.put("/**","authc");
filterFactoryBean.setFilterChainDefinitionMap(map);
Map<String, Filter> filterMap=new LinkedHashMap<>();
//不能把Filter交给spring管理,如果是spring管理,最后都会走到这个Filter,会导致anon失效
filterMap.put("authc",new MyAuthenticationFilter());
filterFactoryBean.setFilters(filterMap);
return filterFactoryBean;
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
MyRealm myRealm=myRealm();
//把密码验证器放到Realm中
myRealm.setCredentialsMatcher(credentialsMatcher());
//添加域到安全管理器中
manager.setRealm(myRealm);
//添加会话管理器到安全管理器中
manager.setSessionManager(webSessionManager());
//添加缓存管理器到安全管理器中
manager.setCacheManager(redisCacheManager);
return manager;
}
/*Session管理器*/
@Bean
public DefaultWebSessionManager webSessionManager() {
MySessionManager manager=new MySessionManager();
//设置Session会话DAO
manager.setSessionDAO(redisSessionDAO);
//禁用cookies
manager.setSessionIdCookieEnabled(false);
//过期时间为1分钟
manager.setGlobalSessionTimeout(1*60000);
//启用会话验证调度器,会话验证调度器是一个后台线程,用于定期检查和验证会话是否过期
manager.setSessionValidationSchedulerEnabled(true);
return manager;
}
@Bean
GDSSRealm myRealm(){return new MyRealm();};
@Bean
MyCredentialsMatcher credentialsMatcher(){return new MyCredentialsMatcher();}
使用:
@Autowired
RedisCacheManager redisCacheManager;
@GetMapping("/login")
public String login(){
//传入参数false,是为了让shiro不自动创建sessionid,而是直接从header拿
//Session session=SecurityUtils.getSubject().getSession(false);
//获取Session,会自动创建Sessionid,如果不想自动创建则传入false参数
Session session=SecurityUtils.getSubject().getSession();
//重新设置过期时间
session.setTimeout(loginTimeout*60000);
//存储
session.setAttribute("name","test name");
//获取
String res = session.getAttribute("name");
//存储cache
redisCacheManager.getCache("ip").put("name","lyy"+ LocalDateTime.now());
return res;
}
redis存储结果: