
springboot+shiro之应用
发布日期:2021-05-27 02:54:25
浏览次数:4
分类:技术文章
本文共 16592 字,大约阅读时间需要 55 分钟。
在web项目日常开发中,轻量级安全框架shiro主要用来进行认证和授权,可以应用在需要对用户登录、角色、url进行权限控制,也可用应用过滤器进行其他校验。权限控制主要通过用户、角色、权限、用户角色关系、角色权限关系进行维护。
- pom文件
4.0.0 org.springframework.boot spring-boot-starter-parent 2.4.3 com springboot 0.0.1-SNAPSHOT springboot Demo project for Spring Boot 1.8 net.sf.json-lib json-lib 2.4 jdk15 org.crazycake shiro-redis 3.2.3 org.apache.shiro shiro-spring 1.4.0 org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin 2.4.3
- application.properties
spring.redis.database=0spring.redis.host=127.0.0.1spring.redis.port=6379spring.redis.password=
- ShiroSessionManager
public class ShiroSessionManager extends DefaultWebSessionManager { /** * 重写构造器 */ public ShiroSessionManager() { super(); this.setDeleteInvalidSessions(true); } /** * 重写方法实现从请求头获取Token便于接口统一 * 每次请求进来,Shiro会去从请求头找REQUEST_HEADER这个key对应的Value(Token) */ @Override public Serializable getSessionId(ServletRequest request, ServletResponse response) { String token = WebUtils.toHttp(request).getHeader("Authorization"); // 如果请求头中存在token 则从请求头中获取token if (!ObjectUtils.isEmpty(token)) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "Stateless request"); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return token; } else { // 否则按默认规则从cookie取token return super.getSessionId(request, response); } }}
-
MyShiroRealm
public class MyShiroRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); // 获取当前登录用户信息 SysUser currentUser = (SysUser) principalCollection.getPrimaryPrincipal(); // 根据用户名称去数据库查询用户、角色、权限信息 ListroleList = roleService.getRoleListByUserId(currentUser.getId()); for (SysRole role : roleList) { // 添加角色 simpleAuthorizationInfo.addRole(role.getName()); } List permissionList = permissionService.getPermissionListByUserId(currentUser.getId()); for (SysPermission permission : permissionList) { // 添加权限 simpleAuthorizationInfo.addStringPermission(permission.getPermission()); } return simpleAuthorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 获取当前登录用户信息 String name = authenticationToken.getPrincipal().toString(); // 根据用户名称去数据库查询用户 SysUser user = userService.getUserByName(name); // 根据userId获取role信息 if (user == null) { //这里返回后会报出对应异常 return null; } else { // 这里验证authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( user, user.getPwd(), // 报序列化错误 // ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt ByteSourceUtils.bytes(user.getCredentialsSalt()),//salt=username+salt getName() ); return simpleAuthenticationInfo; } } public void clearAllCachedAuthorizationInfo() { getAuthorizationCache().clear(); } public void clearAllCachedAuthenticationInfo() { getAuthenticationCache().clear(); } public void clearAllCache() { clearAllCachedAuthenticationInfo(); clearAllCachedAuthorizationInfo(); }}
-
ShiroConfig
@Configurationpublic class ShiroConfig { private final String CACHE_KEY = "shiro:cache:"; private final String SESSION_KEY = "shiro:session:"; // cacheManager、redisSessionDAO 过期时间 private final int EXPIRE = 1800; @Autowired private RedisProperties redisProperties; @Autowired private PermissionService permissionService; @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 自定义过滤器 Mapfilters = new HashMap<>(); // 用户登录状态校验 filters.put("user", new SecurityFormAuthenticationFilter()); // 角色权限校验 filters.put("croles", new SecurityRoleAuthorizationFilter()); // 接口权限校验 filters.put("cperms", new SecurityPermissionAuthorizationFilter()); // 用户退出系统 filters.put("logout", new SecurityLogoutFilter()); shiroFilterFactoryBean.setFilters(filters); // 自定义过滤器链 Map filterChainDefinitionMap = new LinkedHashMap (); // 顺序判断;authc:所有url都必须认证通过才可以访问;anon:所有url都都可以匿名访问 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/getVerifyCode", "anon"); filterChainDefinitionMap.put("/getSid", "anon"); filterChainDefinitionMap.put("/index.html*", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); List permissionInfoList = permissionService.getAllRolePermissionList(); // 数据库读取所有角色、权限信息,拦截校验 for (PermissionInfo permissionInfo : permissionInfoList) { List roleList = permissionInfo.getRoleList(); String roleSet = ""; for (SysRole role : roleList) { roleSet += role.getName() + ","; } filterChainDefinitionMap.put(permissionInfo.getUrl().startsWith("/") ? permissionInfo.getUrl() : "/" + permissionInfo.getUrl(), "user,authc" + ",cperms[" + permissionInfo.getPermission() + "]" + ("".equals(roleSet) ? "" : ",croles[" + roleSet.substring(0, roleSet.length() - 1) + "]")); System.out.println("shiro config: url" + (permissionInfo.getUrl().startsWith("/") ? permissionInfo.getUrl() : "/" + permissionInfo.getUrl()) + "===" + ("user,authc" + ",cperms[" + permissionInfo.getPermission() + "]" + ("".equals(roleSet) ? "" : ",croles[" + roleSet.substring(0, roleSet.length() - 1) + "]"))); } filterChainDefinitionMap.put("/user/hello", "user,authc,croles[super]"); filterChainDefinitionMap.put("/**", "user,authc"); // 登录 //shiroFilterFactoryBean.setLoginUrl("/login"); // 首页 //shiroFilterFactoryBean.setSuccessUrl("/index"); // 错误页面,认证不通过跳转 //shiroFilterFactoryBean.setUnauthorizedUrl("/error"); // 将filterChainDefinitionMap放入过滤器链 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 自定义session管理 securityManager.setSessionManager(sessionManager()); // 自定义Cache实现缓存管理 securityManager.setCacheManager(cacheManager()); // 自定义Realm验证 securityManager.setRealm(myShiroRealm()); return securityManager; } @Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCachingEnabled(true); myShiroRealm.setAuthenticationCachingEnabled(true); myShiroRealm.setAuthorizationCachingEnabled(true); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher(); // 散列算法:这里使用SHA256算法; shaCredentialsMatcher.setHashAlgorithmName("md5"); // 散列的次数,比如散列两次,相当于 md5(md5("")); shaCredentialsMatcher.setHashIterations(2); return shaCredentialsMatcher; } @Bean public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); redisCacheManager.setKeyPrefix(CACHE_KEY); // 配置缓存的话要求放在session里面的实体类必须有个id标识 redisCacheManager.setPrincipalIdFieldName("id"); // 用户权限信息缓存时间 单位秒 此为30分钟 redisCacheManager.setExpire(EXPIRE); return redisCacheManager; } @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setDatabase(redisProperties.getDatabase()); redisManager.setHost(redisProperties.getHost() + ":" + redisProperties.getPort()); // redisManager.setPort(port); // 若在此设置密码则不能取空密码,需设置redis密码 // redisManager.setPassword(redisProperties.getPassword()); return redisManager; } @Bean public SessionManager sessionManager() { ShiroSessionManager shiroSessionManager = new ShiroSessionManager(); shiroSessionManager.setSessionDAO(redisSessionDAO()); return shiroSessionManager; } @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); redisSessionDAO.setSessionIdGenerator(sessionIdGenerator()); // redisSessionDAO.setValueSerializer(new ByteSourceSerializer()); redisSessionDAO.setKeyPrefix(SESSION_KEY); // 此为30分钟 redisSessionDAO.setExpire(EXPIRE); return redisSessionDAO; } @Bean public ShiroSessionIdGenerator sessionIdGenerator() { return new ShiroSessionIdGenerator(); }}
-
SecurityLogoutFilter
public class SecurityLogoutFilter extends LogoutFilter { private static final Logger log = LoggerFactory.getLogger(SecurityLogoutFilter.class); @Value("${safe.switch}") private String safe; @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; Subject subject = getSubject(request, response); SysUser currentUser = (SysUser) subject.getPrincipal(); resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin")); resp.setHeader("Access-Control-Allow-Credentials", "true"); resp.setContentType("application/json; charset=utf-8"); resp.setCharacterEncoding("UTF-8"); String ip = IPUtil.getIpAddress((HttpServletRequest) request); String url = ((HttpServletRequest) request).getRequestURL() + ""; if (currentUser == null) { log.info("用户未登录,无法退出系统"); ResponseUtil.returnResultAjax(resp, JSONUtil.object2JSONStr(ResponseData.fail("用户未登录,无法退出系统",400)), null); } else { logout(subject); log.info("用户" + currentUser.getName() + "正常退出!"); if("on".equals(safe)) { ResponseUtil.returnResultAjax(resp, "退出登录成功", null); }else { ResponseUtil.returnResultAjax(resp, "退出登录成功", null); } } return false; } public static void logout(Subject subject) { if (subject == null) { return; } subject.logout(); }}
- login
@RequestMapping(value = "/login", method = RequestMethod.POST) public Object login(HttpServletRequest request, HttpServletResponse response, @RequestBody Mapparams) { String username = params.get("username"); String pwd = params.get("pwd"); String plainPwd = ""; if ("on".equals(safe)) { //decrypt plainPwd = pwd; } else { plainPwd = pwd; } try { Subject subject = SecurityUtils.getSubject(); System.out.println("登录前sessionId:"+subject.getSession().getId()); // 让旧session失效 subject.logout(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, plainPwd); // 在此清除认证和授权缓存,需去数据库重新获取用户信息 // myShiroRealm.clearAllCache(); subject.login(usernamePasswordToken); System.out.println("登陆成功后sessionId:" + subject.getSession().getId()); SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal(); Map returnData = new HashMap<>(); if (user != null) { returnData.put("userId", String.valueOf(user.getId())); returnData.put("userName", user.getUserName()); returnData.put("token", subject.getSession().getId().toString()); // sessionId与登录用户的对应关系 subject.getSession().setAttribute(subject.getSession().getId().toString(), returnData); // 记录登录用户名与IP之间的关系 subject.getSession().setAttribute(user.getName(), IPUtil.getIpAddress(request)); // 为了让请求头中的Cookie失效 Cookie[] cookies= request.getCookies(); if(cookies!=null){ for (Cookie cookie :cookies){ cookie.setPath("/"); cookie.setHttpOnly(true); cookie.setMaxAge(0); cookie.setValue(""); response.addCookie(cookie); } } } return ResponseData.ok(returnData, "登录成功"); } catch (AuthenticationException ce) { if (ce instanceof UnknownAccountException) { return ResponseData.fail("用户不存在", 1001); } else if (ce instanceof IncorrectCredentialsException) { return ResponseData.fail("密码错误", 1002); } else if (ce instanceof LockedAccountException) { return ResponseData.fail("用户被锁定", 1003); } else if (ce instanceof ExcessiveAttemptsException) { return ResponseData.fail("非法尝试", 1004); } else { return ResponseData.fail("登录出现异常", 999); } } }
访问测试结果
{ "code": 10019, "data": null, "message": "会话过期或未登陆", "status": false}
{ "status": false, "code": 20001, "message": "验证码不正确或已过期,请重新输入或者刷新验证码", "data": null}
{ "code": 19909, "data": null, "message": "url越权访问:http://***", "status": false}
{ "code": 400, "data": null, "message": "用户未登录,无法退出系统", "status": false}
{ "status": true, "code": 200, "message": null, "data": "getUser1"}
转载地址:https://blog.csdn.net/leijie0322/article/details/116571647 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
能坚持,总会有不一样的收获!
[***.219.124.196]2023年11月07日 09时36分46秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
java面试笔记六:并发编程,进程调度算法,线程里面的锁,CAS和ABA问题
2019-03-25
记一次解决Hype-v的端口占用问题
2019-03-25
JVM笔记二:jvm中对象的创建
2019-03-25
Jdk8新特性三:jdk8之函数式编程Function
2019-03-25
jdk8新特性四: jdk8Stream流操作
2019-03-25
springCloud笔记一:架构演进与分布式系统基础知识
2019-03-25
SpringCloud笔记三:服务消费者ribbon和feign和注册中心高可用
2019-03-25
SpringCloud笔记七:微服务下Docker
2019-03-25
SpringCloud笔记九:微服务消息总线Bus结合消息队列RabbitMQ
2019-03-25
jdbc连接数据库代码理解
2019-03-25
jdbc工具类和测试类的的写法理解
2019-03-25
jdbc工具类通过配置文件连接数据库学习理解
2019-03-25
mysql字段的添加、删除、修改
2019-03-25
MySQL学习创建数据库、创建表、添加、删除外键
2019-03-25
Hibernate学习及理解
2019-03-25
java中一些异常的含义更新中有知道其他的可以在评论中说出,我会添加
2019-03-25
List接口学习第一天
2019-03-25
集合的并发修改异常理解
2019-03-25
Mysql安全基线 ===18年毕业设计翻译大家参考一下,外文文献不可抄袭会被查重的
2019-03-25
Map集合的理解包含Map集合的遍历方式迭代器+set集合,和entrySet方式遍历
2019-03-25