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方法就能解決這個小問題。

總結

這一期主要討論了手腳腳須要的東西。

  • 項目基礎依賴
  • 較爲安全的登陸

下期預告

下期目標是產生代碼生成器和菜單樹。


若是你承認我所作的事情,而且認爲我作的事對你有必定的幫助,但願你也能打賞我一杯咖啡,謝謝。

支付寶捐贈
支付寶捐贈
相關文章
相關標籤/搜索