正文开始
首先我们需要简单的看看我们项目需要的支援:
数据库相关:
核心依赖:spring
当然仅仅拥有这些,还不足以完成一个项目的搭建,但是这些是我们构建的基石。使我们更加快速的开发。
怎样来组装一个基础的项目,我们在上一期已经讲完了,本期我们接着要完成一个基础项目框架,同时呢还应该有一个基础项目构建的思考。
1.怎么完成安全校验的登录
其实这个在上一季的项目中已经探讨完成了,这一季只是说老生重谈。甚至来讲,登录是一个简单的过程,却不是个容易的东西。
首先我们应该做到: 可靠、安全、有效 。详细说一下就是:传输过程加密,数据存储加密,信息服务器存放,前端单纯的展示。那么我们常规的处理手段有:
具体的代码如下:
@Controller @Api(description = "外层信息,无需Shiro接管,集成文件下载控制器") public class MainController{ @PostMapping(value = "/login", produces = MediaType.TEXT_HTML_VALUE) @ApiOperation(value = "/login", notes = "登录后台系统") public String login(@ApiParam(hidden = true) ModelMap map, @ApiParam(hidden = true) ShiroHttpServletRequest request, @ApiParam(value = "用户名不能为空,否则不允许登录" , required = true) @RequestParam(value = "userLogin", required = false) String userLogin, @ApiParam(value = "用户密码不能为空且必须为16位小写MD5,否则不允许登录" , required = true) @RequestParam(value = "userPass", required = false) String userPass) { User result = null; try { //1.得到Subject Subject subject = SecurityUtils.getSubject(); //2.调用登录方法 UsernamePasswordToken token = new UsernamePasswordToken(userLogin, userPass); subject.login(token);//当这一代码执行时,就会自动跳入到AuthRealm中认证方法 result = (User) subject.getPrincipal(); subject.getSession().setAttribute("userInfo", result); return "redirect:/endSys/index"; } catch (Exception e) { e.printStackTrace(); LogE.getInstance(this.getClass()).logOutLittle(e.getMessage()); map.addAttribute("msg", e.getMessage()); return "login"; } } @GetMapping(path = "logOut", produces = MediaType.TEXT_HTML_VALUE) @ApiOperation(value = "退出登录", notes = "退出登录,清空session") public String logOut() { Subject subject = SecurityUtils.getSubject(); if (subject.isAuthenticated()) { subject.getSession().removeAttribute("userInfo"); subject.logout(); // session 会销毁,在SessionListener监听session销毁,清理权限缓存 } return "redirect:/"; } } public class ShiroRealm extends AuthorizingRealm { @Autowired private UserServiceImpl userService; /* * 登录信息和用户验证信息验证(non-Javadoc) * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken user = (UsernamePasswordToken) token; LogE.getInstance(ShiroRealm.class).logOutLittle("开始登录====>/n用户为:" + user.getUsername()); String userLogin = user.getUsername(); char[] password = user.getPassword(); User loginResult = null; try { loginResult = userService.login(userLogin, new String(password)); } catch (Exception e) { e.printStackTrace(); LogE.getInstance(ShiroRealm.class).logOutLittle("登录异常结束====>/n用户为:" + user.getUsername()); throw new AuthenticationException(e.getMessage()); } LogE.getInstance(ShiroRealm.class).logOutLittle("登录成功====>/n用户为:" + user.getUsername()); return new SimpleAuthenticationInfo(loginResult, user.getPassword(), this.getName()); } } public class MyCredentialsMatcher extends SimpleCredentialsMatcher { /** * 密码比较方法,有自己的登录校验方法,故此绕过校验 * * @param token * @param info * @return */ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { return true; } }
当然,其他的代码,都不是那么核心,我们主要是围绕着Shiro来阐述我们的登录。
为什么我们的登录和注销地址不要Shiro接管呢?因为无论在登录或注销的时候有没有用户,我们都会执行对应的操作来分别存放用户信息或者清除用户信息。
但是仅仅有这个就能完成登陆校验?错!错!错! 我们需要把shiro接管的页面都纳入管理范围内。也就会产生spring相关的设置,这些,在我们上一季都是有讲到过。但是上一季是XML配置,这一次我们是java配置。
@Configuration public class ShiroConfig { @Bean public ShiroRealm realm() { ShiroRealm myShiroRealm = new ShiroRealm(); MyCredentialsMatcher matcher = new MyCredentialsMatcher(); myShiroRealm.setCredentialsMatcher(matcher); //设置解密规则 return myShiroRealm; } //SecurityManager 是 Shiro 架构的核心,通过它来链接Realm和用户(文档中称之为Subject.) @Bean public DefaultSecurityManager securityManager() { DefaultSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); //将Realm注入到SecurityManager中。 DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setGlobalSessionTimeout(1800000); //默认三十分钟 // Cookie cookie = new SimpleCookie(); //设置cookie // cookie.setName("sid"); //java默认值是JSESSIONID // cookie.setDomain("acheng1314.cn"); //cookie作用域 // cookie.setMaxAge(1800); //cookie超时时间30分钟 // cookie.setHttpOnly(true); // // sessionManager.setSessionIdCookie(cookie); // sessionManager.setSessionIdCookieEnabled(true); //session会话验证 // ExecutorServiceSessionValidationScheduler sessionValidationScheduler = new ExecutorServiceSessionValidationScheduler(); // sessionValidationScheduler.setInterval(3600000); // sessionValidationScheduler.setSessionManager(sessionManager); // // sessionManager.setSessionValidationScheduler(sessionValidationScheduler); // sessionManager.setSessionValidationSchedulerEnabled(true); securityManager.setSessionManager(sessionManager); //此处已经自动持有DefaultWebSessionManager return securityManager; } //在这里配置url访问规则 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/favicon.ico", "anon"); filterChainDefinitionMap.put("/static/*/**", "anon"); //authc表示需要验证身份才能访问,还有一些比如anon表示不需要验证身份就能访问等。 filterChainDefinitionMap.put("/druid/*/**", "authc"); filterChainDefinitionMap.put("/endSys/*/**", "authc"); filterChainDefinitionMap.put("/swagger-ui.html/*/**", "authc"); shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/endSys/index"); // shiroFilterFactoryBean.setUnauthorizedUrl("/403"); //这里设置403并不会起作用,参考http://www.jianshu.com/p/e03f5b54838c shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } }
我们首先告诉spring框架这个是我们框架的设置,需要载入。 然后接着在这个设置里面配置对应的bean(ShiroRealm、DefaultSecurityManager、ShiroFilterFactoryBean)来实现相应的调度规则。
一些具体的细节,如:前端登录页面、数据库操作等等,请查阅 github仓库代码 或者访问码云
到目前这里,我们可以实现登录到系统首页了: http://localhost:8181/login
登录成功后,简单的主页如下:
悄悄的告诉你,我后端主页使用了zDrag来实现网页内部窗体管理。
当然到了这里还是有点小问题,那就是我们用户信息过期后,我们点击菜单会产生内部窗体登录(登录成功后再点击菜单会回到正确界面)我们添加一个js方法就能解决这个小问题。
总结
这一期主要讨论了手脚脚需要的东西。
下期预告
下期目标是产生代码生成器和菜单树。
如果你认可我所做的事情,并且认为我做的事对你有一定的帮助,希望你也能打赏我一杯咖啡,谢谢。