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

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

号外前端

本项目github仓库:github.com/pc859107393…java

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

本系列为连载文章。固然若是你没有spring基础,建议你先看看个人java手把手教程github

固然个人简书访问速度更快

有兴趣交流springboot进行快速开发的同窗能够加一下下面的企鹅群。web

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

正文开始spring

首先咱们须要简单的看看咱们项目须要的支援:数据库

  • 快速部署:spring-boot-devtools
  • 数据库相关:apache

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

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

总结

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

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

下期预告

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


若是你承认我所作的事情,而且认为我作的事对你有必定的帮助,但愿你也能打赏我一杯咖啡,谢谢。

支付宝捐赠
支付宝捐赠
相关文章
相关标签/搜索