From 1809f133ada8949cabca0caa360660f707c66323 Mon Sep 17 00:00:00 2001 From: jinxiaojie <15736965205@163.com> Date: Wed, 11 Nov 2020 14:59:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4shiro-token=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 80 +++--- .../fc/test/controller/AdminController.java | 261 +++++++++--------- .../test/controller/admin/ApiController.java | 4 +- .../test/controller/admin/ApiController2.java | 32 +++ .../com/fc/test/shiro/config/ShiroConfig.java | 130 ++++++--- .../shiro/config/ShiroFilterMapFactory.java | 67 ++--- .../service/CORSAuthenticationFilter.java | 57 ++++ .../fc/test/shiro/service/ShiroSession.java | 69 +++++ .../shiro/service/UuidSessionIdGenerator.java | 18 ++ 9 files changed, 473 insertions(+), 245 deletions(-) create mode 100644 src/main/java/com/fc/test/controller/admin/ApiController2.java create mode 100644 src/main/java/com/fc/test/shiro/service/CORSAuthenticationFilter.java create mode 100644 src/main/java/com/fc/test/shiro/service/ShiroSession.java create mode 100644 src/main/java/com/fc/test/shiro/service/UuidSessionIdGenerator.java diff --git a/pom.xml b/pom.xml index 1c21ccc..9612fff 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 @@ -23,7 +24,7 @@ 1.3.3 2.5 - + @@ -45,15 +46,15 @@ - io.springfox - springfox-swagger2 - 2.9.2 - - - io.springfox - springfox-swagger-ui - 2.9.2 - + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + com.google.code.gson @@ -65,20 +66,17 @@ pagehelper-spring-boot-starter 1.2.5 - + + + mysql mysql-connector-java - - - - + + + com.alibaba @@ -155,12 +153,12 @@ - org.apache.velocity - velocity - 1.7 + org.apache.velocity + velocity + 1.7 - + org.springframework.boot spring-boot-starter-websocket @@ -176,14 +174,14 @@ spring-boot-configuration-processor true - + - javax.mail - mail - 1.4.7 + javax.mail + mail + 1.4.7 - + org.json @@ -191,13 +189,14 @@ 20180813 - ueditor - 1.4.3 + ueditor + 1.4.3 1.8 system ${basedir}/lib/ueditor-1.1.2.jar - + + org.quartz-scheduler quartz @@ -208,7 +207,12 @@ qiniu-java-sdk [7.2.0, 7.2.99] - + + + com.alibaba + fastjson + 1.2.68 + @@ -220,13 +224,13 @@ org.apache.maven.plugins - maven-surefire-plugin - + maven-surefire-plugin + true - + org.mybatis.generator mybatis-generator-maven-plugin @@ -280,6 +284,6 @@ - + diff --git a/src/main/java/com/fc/test/controller/AdminController.java b/src/main/java/com/fc/test/controller/AdminController.java index d4a22df..a1c4821 100644 --- a/src/main/java/com/fc/test/controller/AdminController.java +++ b/src/main/java/com/fc/test/controller/AdminController.java @@ -35,6 +35,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes; /** * 后台方法 + * * @ClassName: HomeController * @author fuce * @date 2019-10-21 00:10 @@ -42,57 +43,60 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes; */ @Controller @RequestMapping("/admin") -public class AdminController extends BaseController{ - private static Logger logger=LoggerFactory.getLogger(AdminController.class); - +public class AdminController extends BaseController { + private static Logger logger = LoggerFactory.getLogger(AdminController.class); + private String prefix = "admin"; - - @ApiOperation(value="首页",notes="首页") + + @ApiOperation(value = "首页", notes = "首页") @GetMapping("/index") public String index(HttpServletRequest request) { - //获取菜单栏 - BootstrapTree bootstrapTree= sysPermissionService.getbooBootstrapTreePerm(ShiroUtils.getUserId()); - request.getSession().setAttribute("bootstrapTree", bootstrapTree); - request.getSession().setAttribute("sessionUserName",ShiroUtils.getUser().getNickname()); - //获取公告信息 - List notices=sysNoticeService.getuserNoticeNotRead(ShiroUtils.getUser(),0); - request.getSession().setAttribute("notices",notices); - return prefix+"/index"; + // 获取菜单栏 + BootstrapTree bootstrapTree = sysPermissionService.getbooBootstrapTreePerm(ShiroUtils.getUserId()); + request.getSession().setAttribute("bootstrapTree", bootstrapTree); + request.getSession().setAttribute("sessionUserName", ShiroUtils.getUser().getNickname()); + // 获取公告信息 + List notices = sysNoticeService.getuserNoticeNotRead(ShiroUtils.getUser(), 0); + request.getSession().setAttribute("notices", notices); + return prefix + "/index"; } - - @ApiOperation(value="局部刷新区域",notes="局部刷新区域") + + @ApiOperation(value = "局部刷新区域", notes = "局部刷新区域") @GetMapping("/main") public String main(ModelMap map) { - setTitle(map, new TitleVo("首页", "首页", true,"欢迎进入", true, false)); - return prefix+"/main"; + setTitle(map, new TitleVo("首页", "首页", true, "欢迎进入", true, false)); + return prefix + "/main"; } - + /** * 请求到登陆界面 + * * @param request * @return */ - @ApiOperation(value="请求到登陆界面",notes="请求到登陆界面") + @ApiOperation(value = "请求到登陆界面", notes = "请求到登陆界面") @GetMapping("/login") - public String login(ModelMap modelMap) { - try { - if ((null != SecurityUtils.getSubject() && SecurityUtils.getSubject().isAuthenticated()) || SecurityUtils.getSubject().isRemembered()) { - return "redirect:/"+prefix+"/index"; - } else { - System.out.println("--进行登录验证..验证开始"); - - modelMap.put("RollVerification", V2Config.getRollVerification()); - System.out.println("V2Config.getRollVerification()>>>"+V2Config.getRollVerification()); - return "login"; - } - } catch (Exception e) { - e.printStackTrace(); - } - return "login"; - } - + public String login(ModelMap modelMap) { + try { + if ((null != SecurityUtils.getSubject() && SecurityUtils.getSubject().isAuthenticated()) + || SecurityUtils.getSubject().isRemembered()) { + return "redirect:/" + prefix + "/index"; + } else { + System.out.println("--进行登录验证..验证开始"); + + modelMap.put("RollVerification", V2Config.getRollVerification()); + System.out.println("V2Config.getRollVerification()>>>" + V2Config.getRollVerification()); + return "login"; + } + } catch (Exception e) { + e.printStackTrace(); + } + return "login"; + } + /** * 用户登陆验证 + * * @param user * @param rcode * @param redirectAttributes @@ -101,122 +105,123 @@ public class AdminController extends BaseController{ * @param request * @return */ - @ApiOperation(value="用户登陆验证",notes="用户登陆验证") + @ApiOperation(value = "用户登陆验证", notes = "用户登陆验证") @PostMapping("/login") @ResponseBody - public AjaxResult login(TsysUser user,String code,RedirectAttributes redirectAttributes,boolean rememberMe,HttpServletRequest request) { - //ModelAndView view =new ModelAndView(); - Boolean yz=false; - if(V2Config.getRollVerification()) {//滚动验证 - yz=true; - }else {//图片验证 - String scode = (String)request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY); - yz=StringUtils.isNotEmpty(scode)&&StringUtils.isNotEmpty(code)&&scode.equals(code); + public AjaxResult login(TsysUser user, String code, RedirectAttributes redirectAttributes, boolean rememberMe, + HttpServletRequest request) { + // ModelAndView view =new ModelAndView(); + Boolean yz = false; + if (V2Config.getRollVerification()) {// 滚动验证 + yz = true; + } else {// 图片验证 + String scode = (String) request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY); + yz = StringUtils.isNotEmpty(scode) && StringUtils.isNotEmpty(code) && scode.equals(code); } - //判断验证码 - if(yz){ - String userName = user.getUsername(); - Subject currentUser = SecurityUtils.getSubject(); - //是否验证通过 - if(!currentUser.isAuthenticated()) { - UsernamePasswordToken token =new UsernamePasswordToken(userName,user.getPassword()); - try { - if(rememberMe) { - token.setRememberMe(true); - } - //存入用户 - currentUser.login(token); - if(StringUtils.isNotNull(ShiroUtils.getUser())) { - //跳转到 get请求的登陆方法 - //view.setViewName("redirect:/"+prefix+"/index"); - return AjaxResult.success(); - }else { - return AjaxResult.error(500,"未知账户"); - } - }catch (UnknownAccountException uae) { - logger.info("对用户[" + userName + "]进行登录验证..验证未通过,未知账户"); - return AjaxResult.error(500,"未知账户"); - } catch (IncorrectCredentialsException ice) { - logger.info("对用户[" + userName + "]进行登录验证..验证未通过,错误的凭证"); - return AjaxResult.error(500, "用户名或密码不正确"); - } catch (LockedAccountException lae) { - logger.info("对用户[" + userName + "]进行登录验证..验证未通过,账户已锁定"); - return AjaxResult.error(500,"账户已锁定"); - } catch (ExcessiveAttemptsException eae) { - logger.info("对用户[" + userName + "]进行登录验证..验证未通过,错误次数过多"); - return AjaxResult.error(500,"用户名或密码错误次数过多"); - } catch (AuthenticationException ae) { - //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景 - logger.info("对用户[" + userName + "]进行登录验证..验证未通过,堆栈轨迹如下"); - ae.printStackTrace(); - return AjaxResult.error(500,"用户名或密码不正确"); - } - }else { - if(StringUtils.isNotNull(ShiroUtils.getUser())) { - //跳转到 get请求的登陆方法 - //view.setViewName("redirect:/"+prefix+"/index"); - return AjaxResult.success(); - }else { - return AjaxResult.error(500,"未知账户"); - } - } - }else{ - return AjaxResult.error(500,"验证码不正确!"); - } - + // 判断验证码 + if (yz) { + String userName = user.getUsername(); + Subject currentUser = SecurityUtils.getSubject(); + // 是否验证通过 + if (!currentUser.isAuthenticated()) { + UsernamePasswordToken token = new UsernamePasswordToken(userName, user.getPassword()); + try { + if (rememberMe) { + token.setRememberMe(true); + } + // 存入用户 + currentUser.login(token); + if (StringUtils.isNotNull(ShiroUtils.getUser())) { + // 若为前后端分离版本,则可把sessionId返回,作为分离版本的请求头authToken + // String authToken = ShiroUtils.getSessionId(); + // return AjaxResult.successData(200, authToken); + return AjaxResult.success(); + } else { + return AjaxResult.error(500, "未知账户"); + } + } catch (UnknownAccountException uae) { + logger.info("对用户[" + userName + "]进行登录验证..验证未通过,未知账户"); + return AjaxResult.error(500, "未知账户"); + } catch (IncorrectCredentialsException ice) { + logger.info("对用户[" + userName + "]进行登录验证..验证未通过,错误的凭证"); + return AjaxResult.error(500, "用户名或密码不正确"); + } catch (LockedAccountException lae) { + logger.info("对用户[" + userName + "]进行登录验证..验证未通过,账户已锁定"); + return AjaxResult.error(500, "账户已锁定"); + } catch (ExcessiveAttemptsException eae) { + logger.info("对用户[" + userName + "]进行登录验证..验证未通过,错误次数过多"); + return AjaxResult.error(500, "用户名或密码错误次数过多"); + } catch (AuthenticationException ae) { + // 通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景 + logger.info("对用户[" + userName + "]进行登录验证..验证未通过,堆栈轨迹如下"); + ae.printStackTrace(); + return AjaxResult.error(500, "用户名或密码不正确"); + } + } else { + if (StringUtils.isNotNull(ShiroUtils.getUser())) { + // 跳转到 get请求的登陆方法 + // view.setViewName("redirect:/"+prefix+"/index"); + return AjaxResult.success(); + } else { + return AjaxResult.error(500, "未知账户"); + } + } + } else { + return AjaxResult.error(500, "验证码不正确!"); + } + } - + /** * 退出登陆 + * * @return */ - @ApiOperation(value="退出登陆",notes="退出登陆") + @ApiOperation(value = "退出登陆", notes = "退出登陆") @GetMapping("/Loginout") - public String LoginOut(HttpServletRequest request, HttpServletResponse response){ - //在这里执行退出系统前需要清空的数据 + public String LoginOut(HttpServletRequest request, HttpServletResponse response) { + // 在这里执行退出系统前需要清空的数据 Subject subject = SecurityUtils.getSubject(); - //注销 - subject.logout(); - return "redirect:/"+prefix+"/login"; + // 注销 + subject.logout(); + return "redirect:/" + prefix + "/login"; } - - - - - /****页面测试****/ - @ApiOperation(value="404页面",notes="404页面") + + /**** 页面测试 ****/ + @ApiOperation(value = "404页面", notes = "404页面") @GetMapping("Out404") - public String Out404(HttpServletRequest request, HttpServletResponse response){ - - return "redirect:/error/404"; + public String Out404(HttpServletRequest request, HttpServletResponse response) { + + return "redirect:/error/404"; } - + @GetMapping("Out403") - @ApiOperation(value="403页面",notes="403页面") - public String Out403(HttpServletRequest request, HttpServletResponse response){ - - return "redirect:/error/403"; + @ApiOperation(value = "403页面", notes = "403页面") + public String Out403(HttpServletRequest request, HttpServletResponse response) { + + return "redirect:/error/403"; } - - @ApiOperation(value="500页面",notes="500页面") + + @ApiOperation(value = "500页面", notes = "500页面") @GetMapping("Out500") - public String Out500(HttpServletRequest request, HttpServletResponse response){ - - return "redirect:/error/500"; + public String Out500(HttpServletRequest request, HttpServletResponse response) { + + return "redirect:/error/500"; } - + /** * 权限测试跳转页面 + * * @param request * @param response * @return */ - @ApiOperation(value="权限测试跳转页面",notes="权限测试跳转页面") + @ApiOperation(value = "权限测试跳转页面", notes = "权限测试跳转页面") @GetMapping("Outqx") @RequiresPermissions("system:user:asd") - public String Outqx(HttpServletRequest request, HttpServletResponse response){ - - return "redirect:/error/500"; + public String Outqx(HttpServletRequest request, HttpServletResponse response) { + + return "redirect:/error/500"; } - /****页面测试EDN****/ + /**** 页面测试EDN ****/ } diff --git a/src/main/java/com/fc/test/controller/admin/ApiController.java b/src/main/java/com/fc/test/controller/admin/ApiController.java index 5789452..a724839 100644 --- a/src/main/java/com/fc/test/controller/admin/ApiController.java +++ b/src/main/java/com/fc/test/controller/admin/ApiController.java @@ -5,6 +5,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.fc.test.common.domain.AjaxResult; +import com.fc.test.shiro.util.ShiroUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -26,7 +27,6 @@ public class ApiController { @GetMapping("/test") @ResponseBody public AjaxResult test() { - - return AjaxResult.success(); + return AjaxResult.successData(200, ShiroUtils.getLoginName()); } } diff --git a/src/main/java/com/fc/test/controller/admin/ApiController2.java b/src/main/java/com/fc/test/controller/admin/ApiController2.java new file mode 100644 index 0000000..babfe11 --- /dev/null +++ b/src/main/java/com/fc/test/controller/admin/ApiController2.java @@ -0,0 +1,32 @@ +package com.fc.test.controller.admin; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import com.fc.test.common.domain.AjaxResult; +import com.fc.test.shiro.util.ShiroUtils; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + +/** + * 该接口为了前后端分离or手机端服务不用登录的接口 访问地址:localhot:8080/ApiController/test + * 如何还需要其他 接口不登陆就能访问:ShiroFilterMapFactory.java里面配置开放自己的controller + * @ClassName: ApiController + * @author fuce + * @date 2020-04-15 22:59 + */ +@Api(value = "api_test") +@Controller +@RequestMapping("/api") +public class ApiController2 { + + //@Log(title = "${TsysTables.tableComment}集合查询", action = "111") + @ApiOperation(value = "测试方法", notes = "测试方法") + @GetMapping("/test") + @ResponseBody + public AjaxResult test() { + return AjaxResult.successData(200, ShiroUtils.getLoginName()); + } +} diff --git a/src/main/java/com/fc/test/shiro/config/ShiroConfig.java b/src/main/java/com/fc/test/shiro/config/ShiroConfig.java index 10ed1f9..b50675e 100644 --- a/src/main/java/com/fc/test/shiro/config/ShiroConfig.java +++ b/src/main/java/com/fc/test/shiro/config/ShiroConfig.java @@ -1,26 +1,38 @@ package com.fc.test.shiro.config; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.servlet.Filter; + import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.MemoryConstrainedCacheManager; import org.apache.shiro.mgt.RememberMeManager; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.realm.Realm; +import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.Cookie; import org.apache.shiro.web.servlet.SimpleCookie; -import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.aspectj.weaver.NewConstructorTypeMunger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; + +import com.fc.test.shiro.service.CORSAuthenticationFilter; import com.fc.test.shiro.service.MyShiroRealm; -import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; +import com.fc.test.shiro.service.ShiroSession; +import com.fc.test.shiro.service.UuidSessionIdGenerator; +import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; /** * 权限配置文件 + * * @ClassName: ShiroConfiguration * @author fuce * @date 2018年8月25日 @@ -31,122 +43,152 @@ public class ShiroConfig { /** * 这是shiro的大管家,相当于mybatis里的SqlSessionFactoryBean + * * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); - //登录 + shiroFilterFactoryBean.setSecurityManager(securityManager); + // 登录 shiroFilterFactoryBean.setLoginUrl("/admin/login"); - //首页 - shiroFilterFactoryBean.setSuccessUrl("/"); - //错误页面,认证不通过跳转 + // 首页 +// shiroFilterFactoryBean.setSuccessUrl("/"); + // 错误页面,认证不通过跳转 shiroFilterFactoryBean.setUnauthorizedUrl("/error/403"); - //页面权限控制 + // 页面权限控制 shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroFilterMapFactory.shiroFilterMap()); - shiroFilterFactoryBean.setSecurityManager(securityManager); + // 自定义拦截器 + Map customFilterMap = new LinkedHashMap<>(); + customFilterMap.put("corsAuthenticationFilter", new CORSAuthenticationFilter()); + shiroFilterFactoryBean.setFilters(customFilterMap); + return shiroFilterFactoryBean; } - + /** * web应用管理配置 + * * @param shiroRealm * @param cacheManager * @param manager * @return */ @Bean - public DefaultWebSecurityManager securityManager(Realm shiroRealm,CacheManager cacheManager,RememberMeManager manager) { + public DefaultWebSecurityManager securityManager(Realm shiroRealm, CacheManager cacheManager, + RememberMeManager manager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setCacheManager(cacheManager); - securityManager.setRememberMeManager(manager);//记住Cookie + securityManager.setRememberMeManager(manager);// 记住Cookie securityManager.setRealm(shiroRealm); securityManager.setSessionManager(sessionManager()); return securityManager; } +// /** +// * session过期控制 +// * @return +// * @author fuce +// * @Date 2019年11月2日 下午12:49:49 +// */ +// @Bean +// public DefaultWebSessionManager sessionManager() { +// DefaultWebSessionManager defaultWebSessionManager=new DefaultWebSessionManager(); +// // 设置session过期时间3600s +// Long timeout=60L*1000*60;//毫秒级别 +// defaultWebSessionManager.setGlobalSessionTimeout(timeout); +// return defaultWebSessionManager; +// } + /** - * session过期控制 + * 自定义的 shiro session 缓存管理器,用于跨域等情况下使用 token 进行验证,不依赖于sessionId + * * @return - * @author fuce - * @Date 2019年11月2日 下午12:49:49 */ @Bean - public DefaultWebSessionManager sessionManager() { - DefaultWebSessionManager defaultWebSessionManager=new DefaultWebSessionManager(); - // 设置session过期时间3600s - Long timeout=60L*1000*60;//毫秒级别 - defaultWebSessionManager.setGlobalSessionTimeout(timeout); - return defaultWebSessionManager; + public SessionManager sessionManager() { + // 将我们继承后重写的shiro session 注册 + ShiroSession shiroSession = new ShiroSession(); + // 如果后续考虑多tomcat部署应用,可以使用shiro-redis开源插件来做session 的控制,或者nginx 的负载均衡 + EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO(); + sessionDAO.setSessionIdGenerator(new UuidSessionIdGenerator()); + shiroSession.setSessionDAO(sessionDAO); + return shiroSession; } + /** * 加密算法 + * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); - hashedCredentialsMatcher.setHashAlgorithmName("MD5");//采用MD5 进行加密 - hashedCredentialsMatcher.setHashIterations(1);//加密次数 + hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 采用MD5 进行加密 + hashedCredentialsMatcher.setHashIterations(1);// 加密次数 return hashedCredentialsMatcher; } - + /** * 记住我的配置 + * * @return */ @Bean public RememberMeManager rememberMeManager() { Cookie cookie = new SimpleCookie("rememberMe"); - cookie.setHttpOnly(true);//通过js脚本将无法读取到cookie信息 - cookie.setMaxAge(60 * 60 * 24);//cookie保存一天 - CookieRememberMeManager manager=new CookieRememberMeManager(); + cookie.setHttpOnly(true);// 通过js脚本将无法读取到cookie信息 + cookie.setMaxAge(60 * 60 * 24);// cookie保存一天 + CookieRememberMeManager manager = new CookieRememberMeManager(); manager.setCookie(cookie); return manager; } + /** * 缓存配置 + * * @return */ @Bean public CacheManager cacheManager() { - MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存 + MemoryConstrainedCacheManager cacheManager = new MemoryConstrainedCacheManager();// 使用内存缓存 return cacheManager; } - + /** * 配置realm,用于认证和授权 + * * @param hashedCredentialsMatcher * @return */ @Bean public AuthorizingRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) { MyShiroRealm shiroRealm = new MyShiroRealm(); - //校验密码用到的算法 + // 校验密码用到的算法 shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher); return shiroRealm; } - + /** * 启用shiro方言,这样能在页面上使用shiro标签 + * * @return */ @Bean - public ShiroDialect shiroDialect() { - return new ShiroDialect(); - } - + public ShiroDialect shiroDialect() { + return new ShiroDialect(); + } + /** - * 启用shiro注解 - *加入注解的使用,不加入这个注解不生效 - */ - @Bean - public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) { - AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); - advisor.setSecurityManager(securityManager); - return advisor; - } - + * 启用shiro注解 加入注解的使用,不加入这个注解不生效 + */ + @Bean + public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor( + org.apache.shiro.mgt.SecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); + advisor.setSecurityManager(securityManager); + return advisor; + } } diff --git a/src/main/java/com/fc/test/shiro/config/ShiroFilterMapFactory.java b/src/main/java/com/fc/test/shiro/config/ShiroFilterMapFactory.java index 5670d76..674b35e 100644 --- a/src/main/java/com/fc/test/shiro/config/ShiroFilterMapFactory.java +++ b/src/main/java/com/fc/test/shiro/config/ShiroFilterMapFactory.java @@ -10,54 +10,55 @@ import java.util.Map; * */ public class ShiroFilterMapFactory { - -/** -anon:例子/admins/**=anon 没有参数,表示可以匿名使用。 - -authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数 - -roles(角色):例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。 - -perms(权限):例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 - -rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。 - -port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString -是你访问的url里的?后面的参数。 + /** + * anon:例子/admins/**=anon 没有参数,表示可以匿名使用。 + * + * authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数 + * + * roles(角色):例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。 + * + * perms(权限):例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 + * + * rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] + * ,其中method为post,get,delete等。 + * + * port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString + * + * 是你访问的url里的?后面的参数。 + * + * authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证 + * + * ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https + * + * user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查 + * + */ -authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证 - -ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https - -user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查 - -*/ - public static Map shiroFilterMap() { // 设置路径映射,注意这里要用LinkedHashMap 保证有序 LinkedHashMap filterChainDefinitionMap = new LinkedHashMap<>(); - //对所有用户认证 + // 对所有用户认证 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/admin/login", "anon"); filterChainDefinitionMap.put("/admin/logout", "logout"); - //放验证码 + // 放验证码 filterChainDefinitionMap.put("/captcha/**", "anon"); // 释放 druid 监控画面 filterChainDefinitionMap.put("/druid/**", "anon"); - //释放websocket请求 + // 释放websocket请求 filterChainDefinitionMap.put("/websocket", "anon"); - //前端 + // 前端 filterChainDefinitionMap.put("/", "anon"); - filterChainDefinitionMap.put("/index", "anon");//任务调度暂时放开 - + filterChainDefinitionMap.put("/index", "anon");// 任务调度暂时放开 + filterChainDefinitionMap.put("/quartz/**", "anon"); - - //开放APicontroller + + // 开放APicontroller filterChainDefinitionMap.put("/ApiController/**", "anon"); - - //对所有页面进行认证 - filterChainDefinitionMap.put("/**","authc"); + + // 对所有页面进行认证 + filterChainDefinitionMap.put("/**", "authc"); return filterChainDefinitionMap; } } diff --git a/src/main/java/com/fc/test/shiro/service/CORSAuthenticationFilter.java b/src/main/java/com/fc/test/shiro/service/CORSAuthenticationFilter.java new file mode 100644 index 0000000..a450f26 --- /dev/null +++ b/src/main/java/com/fc/test/shiro/service/CORSAuthenticationFilter.java @@ -0,0 +1,57 @@ +package com.fc.test.shiro.service; + +import java.io.PrintWriter; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; + +import com.alibaba.fastjson.JSON; +import com.fc.test.common.domain.AjaxResult; + +/** + * @author :LX + * 创建时间: 2019/5/31. 10:25 + * 地点:广州 + * 目的: 过滤OPTIONS请求 继承shiro 的form表单过滤器,对 + * OPTIONS 请求进行过滤。 前后端分离项目中,由于跨域,会导致复杂请求,即会发送preflighted + * request,这样会导致在GET/POST等请求之前会先发一个OPTIONS请求,但OPTIONS请求并不带shiro + * 的'authToken'字段(shiro的SessionId),即OPTIONS请求不能通过shiro验证,会返回未认证的信息。 + * + *备注说明: 需要在 shiroConfig 进行注册 + */ +public class CORSAuthenticationFilter extends FormAuthenticationFilter { + + /** + * 直接过滤可以访问的请求类型 + */ + private static final String REQUET_TYPE = "OPTIONS"; + + public CORSAuthenticationFilter() { + super(); + } + + @Override + public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + if (((HttpServletRequest) request).getMethod().toUpperCase().equals(REQUET_TYPE)) { + return true; + } + return super.isAccessAllowed(request, response, mappedValue); + } + + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { + HttpServletResponse res = (HttpServletResponse) response; + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setStatus(HttpServletResponse.SC_OK); + res.setCharacterEncoding("UTF-8"); + PrintWriter writer = res.getWriter(); +// ResultJson resultJson = new ResultJson(Constant.ERROR_CODE_NO_LOGIN, ResultEnum.ERROR.getStatus(), "请先登录系统!", null); + writer.write(JSON.toJSONString(AjaxResult.error(500, "请先登录系统!"))); + writer.close(); + return false; + } +} \ No newline at end of file diff --git a/src/main/java/com/fc/test/shiro/service/ShiroSession.java b/src/main/java/com/fc/test/shiro/service/ShiroSession.java new file mode 100644 index 0000000..a7b618f --- /dev/null +++ b/src/main/java/com/fc/test/shiro/service/ShiroSession.java @@ -0,0 +1,69 @@ +package com.fc.test.shiro.service; + +import java.io.Serializable; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +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; + +/** + * 原文链接:https://my.oschina.net/sprouting/blog/3059282 + * + * @author :LX + * 创建时间: 2019/5/30. 18:08 + * 地点:广州 + * 目的: shiro 的 session 管理 + * 自定义session规则,实现前后分离,在跨域等情况下使用token 方式进行登录验证才需要,否则没必须使用本类。 shiro默认使用 + * ServletContainerSessionManager 来做 session 管理,它是依赖于浏览器的 cookie 来维护 + * session 的,调用 storeSessionId 方法保存sesionId 到 cookie中 为了支持无状态会话,我们就需要继承 + * DefaultWebSessionManager 自定义生成sessionId 则要实现 SessionIdGenerator + * 备注说明: + */ +public class ShiroSession extends DefaultWebSessionManager { + + /** + * 定义的请求头中使用的标记key,用来传递 token + */ + private static final String AUTH_TOKEN = "authToken"; + + private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; + + public ShiroSession() { + super(); + // 设置 shiro session 失效时间,默认为30分钟,这里现在设置为15分钟 + // setGlobalSessionTimeout(MILLIS_PER_MINUTE * 15); + } + + /** + * 获取sessionId,原本是根据sessionKey来获取一个sessionId + * 重写的部分多了一个把获取到的token设置到request的部分。这是因为app调用登陆接口的时候,是没有token的,登陆成功后,产生了token,我们把它放到request中,返回结 + * 果给客户端的时候,把它从request中取出来,并且传递给客户端,客户端每次带着这个token过来,就相当于是浏览器的cookie的作用,也就能维护会话了 + * + * @param request + * @param response + * @return + */ + @Override + protected Serializable getSessionId(ServletRequest request, ServletResponse response) { + // 获取请求头中的 AUTH_TOKEN 的值,如果请求头中有 AUTH_TOKEN 则其值为sessionId。shiro就是通过sessionId + // 来控制的 + String sessionId = WebUtils.toHttp(request).getHeader(AUTH_TOKEN); + if (StringUtils.isEmpty(sessionId)) { + // 如果没有携带id参数则按照父类的方式在cookie进行获取sessionId + return super.getSessionId(request, response); + + } else { + // 请求头中如果有 authToken, 则其值为sessionId + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); + // sessionId + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId); + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); + return sessionId; + } + } + +} diff --git a/src/main/java/com/fc/test/shiro/service/UuidSessionIdGenerator.java b/src/main/java/com/fc/test/shiro/service/UuidSessionIdGenerator.java new file mode 100644 index 0000000..17b884b --- /dev/null +++ b/src/main/java/com/fc/test/shiro/service/UuidSessionIdGenerator.java @@ -0,0 +1,18 @@ +package com.fc.test.shiro.service; + +import java.io.Serializable; + +import org.apache.shiro.session.Session; +import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator; +import org.apache.shiro.session.mgt.eis.SessionIdGenerator; + +public class UuidSessionIdGenerator implements SessionIdGenerator { + + @Override + public Serializable generateId(Session session) { + // TODO Auto-generated method stub + Serializable uuid = new JavaUuidSessionIdGenerator().generateId(session); + return uuid; + } + +} -- Gitee