美文网首页Java服务器端编程
springBoot探索(2)——构建手脚架

springBoot探索(2)——构建手脚架

作者: Clone丶记忆 | 来源:发表于2017-09-23 04:08 被阅读363次

    已经有一个多月没有更新这个系列的文章了。本期目标是完成基础的手脚架。

    号外

    本项目github仓库:https://github.com/pc859107393/MySpringBoot.git

    本项目国内码云仓库:https://git.oschina.net/859107393/MySpringBoot.git

    本系列为连载文章。当然如果你没有spring基础,建议你先看看我的java手把手教程

    当然我的简书访问速度更快

    有兴趣交流springboot进行快速开发的同学可以加一下下面的企鹅群。

    行走的java全栈行走的java全栈

    正文开始

    首先我们需要简单的看看我们项目需要的支援:

    • 快速部署:spring-boot-devtools

    • 数据库相关:

      • Mybatis
      • Mybatis-plus(常规crud、自带分页)
      • Druid
      • 数据库链接
    • 核心依赖:spring

    • web处理:springMvc

    • 权限和鉴定:Shiro

    • 网络通信:okhttp

    • json解析:gson、fastJson

    • 在线APIDocs:SpringFox

    • 模板引擎:freemarker

    • 等等···

    当然仅仅拥有这些,还不足以完成一个项目的搭建,但是这些是我们构建的基石。使我们更加快速的开发。

    怎样来组装一个基础的项目,我们在上一期已经讲完了,本期我们接着要完成一个基础项目框架,同时呢还应该有一个基础项目构建的思考。

    1.怎么完成安全校验的登录

    其实这个在上一季的项目中已经探讨完成了,这一季只是说老生重谈。甚至来讲,登录是一个简单的过程,却不是个容易的东西。

    首先我们应该做到:可靠、安全、有效。详细说一下就是:传输过程加密,数据存储加密,信息服务器存放,前端单纯的展示。那么我们常规的处理手段有:

    • 登录密码传输前加密
    • 密文强效验
    • 用户信息缓存到session

    具体的代码如下:

    @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方法就能解决这个小问题。

    总结

    这一期主要讨论了手脚脚需要的东西。

    • 项目基础依赖
    • 较为安全的登录

    下期预告

    下期目标是产生代码生成器和菜单树。


    如果你认可我所做的事情,并且认为我做的事对你有一定的帮助,希望你也能打赏我一杯咖啡,谢谢。

    支付宝捐赠支付宝捐赠

    相关文章

      网友评论

        本文标题:springBoot探索(2)——构建手脚架

        本文链接:https://www.haomeiwen.com/subject/eciqextx.html