在前后端分离项目中使用SpringBoot集成Shiro
2019-08-16 09:18:03来源:博客园 阅读 ()
在前后端分离项目中使用SpringBoot集成Shiro
前言
这次在处理一个小项目时用到了前后端分离,服务端使用springboot2.x。权限验证使用了Shiro。前后端分离首先需要解决的是跨域问题,POST接口跨域时会预发送一个OPTIONS请求,浏览器收到响应后会继续执行POST请求。 前后端分离后为了保持会话状态使用session持久化插件shiro-redis,持久化session可以持久化到关系型数据库,也可以持久化到非关系型数据库(主要是重写SessionDao)。Shiro已提供了SessionDao接口和抽象类。如果项目中用到Swagger的话,还需要把swagger相关url放行。
搭建依赖
<dependency> <!--session持久化插件--> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>3.2.3</version> </dependency> <dependency> <!--spring shiro依赖--> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.1</version> </dependency>
Shiro权限配置
1、ShiroConfig。这里主要是shiro核心配置。比如SecurityManager、SessionManager、CacheManager。
public class ShiroConfig { @Value("${spring.redis.shiro.host}") private String host; @Value("${spring.redis.shiro.port}") private int port; @Value("${spring.redis.shiro.timeout}") private int timeout; @Value("${spring.redis.shiro.password}") private String password; /** * 权限规则配置 **/ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, Filter> filters = shiroFilterFactoryBean.getFilters(); filters.put("authc", new MyFormAuthorizationFilter()); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //swagger资源不拦截 filterChainDefinitionMap.put("/swagger-ui.html", "anon"); filterChainDefinitionMap.put("/swagger-resources/**/**", "anon"); filterChainDefinitionMap.put("/v2/api-docs", "anon"); filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon"); filterChainDefinitionMap.put("/configuration/security", "anon"); filterChainDefinitionMap.put("/configuration/ui", "anon"); filterChainDefinitionMap.put("/login/ajaxLogin", "anon"); filterChainDefinitionMap.put("/login/unauth", "anon"); filterChainDefinitionMap.put("/login/logout", "anon"); filterChainDefinitionMap.put("/login/register","anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setLoginUrl("/login/unauth"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * shiro安全管理器(权限验证核心配置) **/ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); securityManager.setSessionManager(sessionManager()); securityManager.setCacheManager(cacheManager()); return securityManager; } /** * 会话管理 **/ @Bean public SessionManager sessionManager() { MySessionManager sessionManager = new MySessionManager(); sessionManager.setSessionIdUrlRewritingEnabled(false); //取消登陆跳转URL后面的jsessionid参数 sessionManager.setSessionDAO(sessionDAO()); sessionManager.setGlobalSessionTimeout(-1);//不过期 return sessionManager; } /** * 使用的是shiro-redis开源插件 缓存依赖 **/ @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host+":"+port); redisManager.setTimeout(timeout); redisManager.setPassword(password); return redisManager; } /** * 使用的是shiro-redis开源插件 session持久化 **/ public RedisSessionDAO sessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } /** * 缓存管理 **/ @Bean public CacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } /** * 权限管理 **/ @Bean public MyShiroRealm myShiroRealm() { return new MyShiroRealm(); } }
2、MyShiroRealm 用户身份验证、自定义权限。
public class MyShiroRealm extends AuthorizingRealm { private Logger logger= LoggerFactory.getLogger(MyShiroRealm.class); @Resource UserDao userDao; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { logger.info("===================权限验证=================="); return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken; User currentUser=userDao.findUser(token.getUsername()); if(null == currentUser){ throw new AuthenticationException("账户不存在"); } if(!currentUser.getPassword().equals(new String(token.getPassword()))){ throw new IncorrectCredentialsException("账户密码不正确"); } if(currentUser.getIsdel()==1){ throw new LockedAccountException("账户已冻结"); } Subject subject = SecurityUtils.getSubject(); BIUser biUser=new BIUser(); biUser.setUserId(currentUser.getUserId()); biUser.setOrgId(currentUser.getOrgid()); biUser.setUserName(currentUser.getUsername()); biUser.setPassword(currentUser.getPassword()); biUser.setSessionId(subject.getSession().getId().toString()); biUser.setIsdel(currentUser.getIsdel()); biUser.setCreateTime(currentUser.getCreatetime()); logger.info("======已授权"+biUser.toString()+"===="); return new SimpleAuthenticationInfo(biUser,biUser.getPassword(),biUser.getUserName()); } }
3、MySessionManager。shiro权限验证是根据客户端Cookie中的JSESSIONID值来确定身份是否合格。前后端分离后这个地方需要处理。客户端调用服务端登陆接口,验证通过后返回给客户端一个token值(这里我放的是sessionid)。客户端保存token值,然后调用其他接口时把token值放在header中。对前端来说也就是放在ajax的headers参数中。
public class MySessionManager extends DefaultWebSessionManager { private static final String AUTHORIZATION = "Authorization"; private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; public MySessionManager() { } @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { //从前端ajax headers中获取这个参数用来判断授权 String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION); if (StringUtils.hasLength(id)) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return id; } else { //从前端的cookie中取值 return super.getSessionId(request, response); } } }
4、MyFormAuthorizationFilter。对于跨域的POST请求,浏览器发起POST请求前都会发送一个OPTIONS请求已确定服务器是否可用,OPTIONS请求通过后继续执行POST请求,而shiro自带的权限验证是无法处理OPTIONS请求的,所以这里需要重写isAccessAllowed方法。
public class MyFormAuthorizationFilter extends FormAuthenticationFilter { protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) { HttpServletRequest httpServletRequest = WebUtils.toHttp(servletRequest); if ("OPTIONS".equals(httpServletRequest.getMethod())) { return true; } return super.isAccessAllowed(servletRequest, servletResponse, o); } }
5、处理跨域
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("PUT", "DELETE", "GET", "POST") .allowedHeaders("*") .exposedHeaders("access-control-allow-headers", "access-control-allow-methods", "access-control-allow" + "-origin", "access-control-max-age", "X-Frame-Options","Authorization") .allowCredentials(false).maxAge(3600); }
原文链接:https://www.cnblogs.com/sword-successful/p/11093803.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:main方法中窥世界
下一篇:十二:命令模式(人员解耦和)
- SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后 2020-06-10
- 初识Nginx——前后端发布 2020-06-03
- java 在线网络考试系统源码 springboot mybaits vue.js 前后 2020-05-31
- 在滴滴和头条干了 2 年后端开发,太真实… 2020-05-27
- 后端程序员就靠它吃饭了,推荐一份夯实基础的操作系统书单! 2020-05-16
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash