1.引入核心依赖
<!--shiro和springboot整合的依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>2.4.6</version>
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
2.编写shiro配置类
注意:连接的redis请改为自己服务器ip和端口
在编写shiro配置类之前还需编写你自己的WebSessionManager
package com.zjh.config;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
/**
* @program: qy165-shiro-framework
* @description:
* @author: 赵家豪
* @create: 2024-05-11 14:38
**/
public class MyWebSessionManager extends DefaultWebSessionManager {
private static final String AUTHORIZATION = "token";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
//获取请求头中名称为token的内容
String id = WebUtils.toHttp(request).getHeader("token");
if (!StringUtils.isEmpty(id)) { //如果存在该token
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "Stateless request");
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
} else {
//从cookie中获取sessionId.
return super.getSessionId(request, response);
}
}
}
package com.zjh.config;
import com.zjh.realm.MyRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean(value = "mysecurityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
//设置缓存管理器
securityManager.setCacheManager(cacheManager());
//设置sessionManager会话管理器--自定义会话对象
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean
public SessionManager sessionManager() {
MyWebSessionManager myWebSessionManager = new MyWebSessionManager();
//为sessionManager指定sessionDao对象
myWebSessionManager.setSessionDAO(sessionDAO());
return myWebSessionManager;
}
@Bean
public SessionDAO sessionDAO() {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
return sessionDAO;
}
@Bean
public CacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
//设置redis管理器
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private Integer port;
@Value("${spring.redis.database}")
private Integer database;
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setDatabase(database);
return redisManager;
}
@Bean
public MyRealm myRealm() {
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(credentialsMatcher());
return myRealm;
}
@Bean
public CredentialsMatcher credentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashIterations(5);
credentialsMatcher.setHashAlgorithmName("MD5");
return credentialsMatcher;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager());
filterFactoryBean.setLoginUrl("/unLogin");
Map<String, String> map = new HashMap<>();
map.put("/login", "anon");
map.put("/**", "authc");
filterFactoryBean.setFilterChainDefinitionMap(map);
return filterFactoryBean;
}
// 注册shiro过滤器
@Bean
public FilterRegistrationBean<Filter> registrationBean() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setName("shiroFilter");
registrationBean.setFilter(new DelegatingFilterProxy());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
3.编写全局异常处理类,处理权限不足异常
package com.zjh.exception;
import com.zjh.vo.Res;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @program: shiro-framework
* @description:
* @author: 赵家豪
* @create: 2024-05-10 15:05
**/
@RestControllerAdvice
public class MyGlobalException {
@ExceptionHandler(value = UnauthorizedException.class)
public Res exception01(UnauthorizedException e) {
return new Res(401, "权限不足", null);
}
}
4.编写realm类
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//这里根据你自己查询的实体类可以进行修改
User user = (User) principals.getPrimaryPrincipal();
//根据id查询该用户具有的权限
List<Permission> permissions = permissionService.selectByUserId(user.getUserid());
if (permissions.size() > 0) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<String> list = permissions.stream().map(item -> item.getPercode()).collect(Collectors.toList());
info.addStringPermissions(list);
return info;
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取输入账号的名称
Object username = token.getPrincipal();
//调用UserService中根据名称查询用户信息
User user = userService.selectByName(username);
//判断user是否为null
if (user != null) {
ByteSource salt = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getUserpwd(), salt, this.getName());
return info;
}
return null;
}
}
5.让自己的实体类实现序列化
不实现序列化无法将用户的信息放到redis中
6.编写application.properties配置文件
根据自己的实际连接的数据库,端口,服务器ip和端口填写。
spring.datasource.url=jdbc:mysql://localhost:3306/shiro
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
server.port=8080
spring.redis.host=192.168.145.128
spring.redis.port=6379
spring.redis.database=2