Browse Source

1.新增验证码开关,动态开启验证码校验
2.新增swagger配置,只生成REST接口,默认配置全局token参数
3.beetl增加全局参数配置,可根据模式根据需要扩展

zhouchenglin 6 years ago
parent
commit
3fde28c127

File diff suppressed because it is too large
+ 14 - 20
doc/dp-boot-security.sql


+ 35 - 0
pom.xml

@@ -7,6 +7,9 @@
     <artifactId>dp</artifactId>
     <version>0.0.1-SNAPSHOT</version>
     <packaging>jar</packaging>
+    <!-- 生成war时放开注释,以及第58行注释
+    <packaging>war</packaging>
+    -->
 
     <name>dp-lte-boot</name>
     <description>dp-lte开发框架SpringBoot版本</description>
@@ -39,6 +42,9 @@
         <quartz-version>2.2.3</quartz-version>
         <httpclient-version>4.5.3</httpclient-version>
         <beetl-version>1.1.60.RELEASE</beetl-version>
+        <swagger-version>2.9.2</swagger-version>
+        <servlet-version>3.1.0</servlet-version>
+        <kaptcha-version>2.3.2</kaptcha-version>
     </properties>
 
     <dependencies>
@@ -50,6 +56,14 @@
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
+            <!-- 生成war时放开注释
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
+            -->
         </dependency>
         <dependency>
             <groupId>com.ibeetl</groupId>
@@ -158,6 +172,27 @@
             <groupId>redis.clients</groupId>
             <artifactId>jedis</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>${swagger-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${swagger-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.penggle</groupId>
+            <artifactId>kaptcha</artifactId>
+            <version>${kaptcha-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet-version}</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>

+ 20 - 1
src/main/java/net/chenlin/dp/DpApplication.java

@@ -5,16 +5,22 @@ import org.slf4j.LoggerFactory;
 import org.springframework.boot.Banner;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 
 /**
  * 应用启动器
  * @author zcl<yczclcn@163.com>
  */
 @SpringBootApplication
-public class DpApplication {
+public class DpApplication extends SpringBootServletInitializer {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(DpApplication.class);
 
+    /**
+     * jar启动
+     * @param args
+     */
     public static void main(String[] args) {
         SpringApplication application = new SpringApplication(DpApplication.class);
         application.setBannerMode(Banner.Mode.OFF);
@@ -22,4 +28,17 @@ public class DpApplication {
         LOGGER.info("The Dp application has been started successfully!");
     }
 
+    /**
+     * war启动
+     * @param builder
+     * @return
+     */
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
+        builder.bannerMode(Banner.Mode.OFF);
+        SpringApplicationBuilder applicationBuilder = builder.sources(DpApplication.class);
+        LOGGER.info("The Dp application has been started successfully!");
+        return applicationBuilder;
+    }
+
 }

+ 5 - 0
src/main/java/net/chenlin/dp/common/support/config/BeetlConfig.java

@@ -1,6 +1,7 @@
 package net.chenlin.dp.common.support.config;
 
 import net.chenlin.dp.common.support.properties.BeetlProperties;
+import net.chenlin.dp.common.support.properties.GlobalProperties;
 import org.beetl.core.resource.ClasspathResourceLoader;
 import org.beetl.ext.spring.BeetlGroupUtilConfiguration;
 import org.beetl.ext.spring.BeetlSpringViewResolver;
@@ -18,6 +19,9 @@ public class BeetlConfig {
     @Autowired
     BeetlProperties beetlProperties;
 
+    @Autowired
+    GlobalProperties globalProperties;
+
     /**
      * beetl的配置
      */
@@ -26,6 +30,7 @@ public class BeetlConfig {
         BeetlGroupUtilConfiguration beetlGroupUtilConfiguration = new BeetlGroupUtilConfiguration();
         beetlGroupUtilConfiguration.setResourceLoader(new ClasspathResourceLoader(BeetlConfig.class.getClassLoader(), beetlProperties.getPrefix()));
         beetlGroupUtilConfiguration.setConfigProperties(beetlProperties.getProperties());
+        beetlGroupUtilConfiguration.setSharedVars(globalProperties.getBeetlGlobalVars());
         return beetlGroupUtilConfiguration;
     }
 

+ 1 - 0
src/main/java/net/chenlin/dp/common/support/config/ShiroConfig.java

@@ -99,6 +99,7 @@ public class ShiroConfig {
         filterMap.put("/static/**", "anon");
         filterMap.put("/error/**", "anon");
         filterMap.put("/login", "anon");
+        filterMap.put("/captcha.jpg", "anon");
         filterMap.put("/rest/**", "anon");
         filterMap.put("/**", "user");
         shiroFilter.setFilterChainDefinitionMap(filterMap);

+ 60 - 0
src/main/java/net/chenlin/dp/common/support/config/SwaggerConfig.java

@@ -0,0 +1,60 @@
+package net.chenlin.dp.common.support.config;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.ParameterBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.schema.ModelRef;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.Parameter;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * swagger配置
+ * @author zcl<yczclcn@163.com>
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    @Bean
+    public Docket createRestApi() {
+        Parameter token = new ParameterBuilder()
+                .name("token")
+                .description("授权码")
+                .required(true)
+                .order(1)
+                .modelRef(new ModelRef("string"))
+                .parameterType("header")
+                .build();
+        List<Parameter> globalParams = new ArrayList<>(1);
+        globalParams.add(token);
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                .paths(PathSelectors.any())
+                .build()
+                .globalOperationParameters(globalParams);
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("Dp Rest Api Doc")
+                .description("Gp Api 文档")
+                .termsOfServiceUrl("https://gitee.com/dp_group")
+                .contact(new Contact("Mr.lin", "www.chenlintect.com", "yczclcn@163.com"))
+                .version("1.0.0")
+                .build();
+    }
+
+}

+ 24 - 0
src/main/java/net/chenlin/dp/common/support/config/WebConfig.java

@@ -1,5 +1,7 @@
 package net.chenlin.dp.common.support.config;
 
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
 import net.chenlin.dp.common.support.interceptor.RestApiInterceptor;
 import net.chenlin.dp.common.support.properties.GlobalProperties;
 import net.chenlin.dp.common.xss.XssFilter;
@@ -20,6 +22,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 import javax.servlet.DispatcherType;
 import java.io.File;
+import java.util.Properties;
 
 /**
  * web配置
@@ -50,6 +53,8 @@ public class WebConfig implements WebMvcConfigurer, ErrorPageRegistrar {
         }
         registry.addResourceHandler(globalProperties.getRegisterUploadMapping())
                 .addResourceLocations(globalProperties.getRegisterUploadLocation());
+        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
     }
 
     /**
@@ -103,4 +108,23 @@ public class WebConfig implements WebMvcConfigurer, ErrorPageRegistrar {
         ErrorPage sysError = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");
         registry.addErrorPages(notFound, sysError);
     }
+
+    /**
+     * 验证码生成相关
+     */
+    @Bean
+    public DefaultKaptcha kaptcha() {
+        Properties properties = new Properties();
+        properties.put("kaptcha.border", "no");
+        properties.put("kaptcha.textproducer.font.color", "black");
+        properties.put("kaptcha.image.width", "136");
+        properties.put("kaptcha.image.height", "50");
+        properties.put("kaptcha.textproducer.char.space", "3");
+        properties.put("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
+        Config config = new Config(properties);
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+
 }

+ 24 - 0
src/main/java/net/chenlin/dp/common/support/properties/GlobalProperties.java

@@ -4,6 +4,9 @@ import org.apache.commons.lang.StringUtils;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * 系统全局配置
  * @author zcl<yczclcn@163.com>
@@ -23,6 +26,19 @@ public class GlobalProperties {
     /** 是否开启redis会话管理器 **/
     private boolean redisSessionDao;
 
+    /** 是否开启验证码 **/
+    private boolean kaptchaEnable;
+
+    /**
+     * beetl全局变量
+     * @return
+     */
+    public Map<String, Object> getBeetlGlobalVars() {
+        Map<String, Object> vars = new HashMap<>(1);
+        vars.put("kaptchaEnable", kaptchaEnable);
+        return vars;
+    }
+
     /**
      * WebConfig注册上传路径
      * @return
@@ -69,4 +85,12 @@ public class GlobalProperties {
         this.redisSessionDao = redisSessionDao;
     }
 
+    public boolean isKaptchaEnable() {
+        return kaptchaEnable;
+    }
+
+    public void setKaptchaEnable(boolean kaptchaEnable) {
+        this.kaptchaEnable = kaptchaEnable;
+    }
+
 }

+ 11 - 0
src/main/java/net/chenlin/dp/common/utils/ShiroUtils.java

@@ -77,4 +77,15 @@ public class ShiroUtils {
 		SecurityUtils.getSubject().logout();
 	}
 
+	/**
+	 * 获取验证码
+	 * @param key
+	 * @return
+	 */
+	public static String getKaptcha(String key) {
+		String kaptcha = getSessionAttribute(key).toString();
+		getSession().removeAttribute(key);
+		return kaptcha;
+	}
+
 }

+ 18 - 30
src/main/java/net/chenlin/dp/modules/api/controller/RestAuthController.java

@@ -1,6 +1,9 @@
 package net.chenlin.dp.modules.api.controller;
 
-import net.chenlin.dp.common.annotation.RestAnon;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import net.chenlin.dp.common.constant.RestApiConstant;
 import net.chenlin.dp.common.entity.R;
 import net.chenlin.dp.common.utils.MD5Utils;
@@ -12,12 +15,15 @@ import net.chenlin.dp.modules.sys.service.SysUserService;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 /**
  * rest授权controller
  * @author zcl<yczclcn@163.com>
  */
+@Api(value = "用户授权", description = "用户授权")
 @RestController
 public class RestAuthController extends AbstractController {
 
@@ -28,16 +34,17 @@ public class RestAuthController extends AbstractController {
      * 登录授权校验
      * @return
      */
-    @RequestMapping(RestApiConstant.AUTH_REQUEST)
-    public R auth() {
-        String username = getParam("username").trim();
-        String password = getParam("password").trim();
+    @ApiOperation(value = "登录")
+    @ApiImplicitParam(name = "token", value = "授权码")
+    @RequestMapping(value = RestApiConstant.AUTH_REQUEST, method = RequestMethod.POST)
+    public R auth(@ApiParam(name = "username", value = "用户名") @RequestParam String username,
+                  @ApiParam(name = "password", value = "密码") @RequestParam String password) {
         // 用户名为空
-        if (StringUtils.isBlank(username)) {
+        if (StringUtils.isBlank(username.trim())) {
             return RestApiConstant.TokenErrorEnum.USER_USERNAME_NULL.getResp();
         }
         // 密码为空
-        if (StringUtils.isBlank(password)) {
+        if (StringUtils.isBlank(password.trim())) {
             return RestApiConstant.TokenErrorEnum.USER_PASSWORD_NULL.getResp();
         }
         // 用户名不存在
@@ -69,11 +76,11 @@ public class RestAuthController extends AbstractController {
      * 异步校验token,用于接口异步校验登录状态
      * @return
      */
-    @RequestMapping(RestApiConstant.AUTH_CHECK)
-    public R authStatus() {
-        String token = getParam(RestApiConstant.AUTH_TOKEN);
+    @ApiOperation(value = "校验token是否可用")
+    @RequestMapping(value = RestApiConstant.AUTH_CHECK, method = RequestMethod.POST)
+    public R authStatus(@ApiParam(name = "token", value = "授权码") @RequestParam String token) {
         // token为空
-        if (StringUtils.isBlank(token)) {
+        if (StringUtils.isBlank(token.trim())) {
             return RestApiConstant.TokenErrorEnum.TOKEN_NOT_FOUND.getResp();
         }
         SysUserTokenEntity sysUserTokenEntity = sysUserService.getUserTokenByToken(token);
@@ -97,23 +104,4 @@ public class RestAuthController extends AbstractController {
         return RestApiConstant.TokenErrorEnum.TOKEN_ENABLE.getResp();
     }
 
-    /**
-     * 验证拦截
-     * @return
-     */
-    @RequestMapping("/rest/testAuth")
-    public String test() {
-        return "auth token";
-    }
-
-    /**
-     * 匿名调用:@RestAnon
-     * @return
-     */
-    @RequestMapping("/rest/testAnon")
-    @RestAnon
-    public String testAnon() {
-        return "rest anon";
-    }
-
 }

+ 42 - 0
src/main/java/net/chenlin/dp/modules/api/controller/RestTestController.java

@@ -0,0 +1,42 @@
+package net.chenlin.dp.modules.api.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import net.chenlin.dp.common.annotation.RestAnon;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 测试接口
+ * @author zcl<yczclcn@163.com>
+ */
+@Api(value = "测试接口", description = "测试接口")
+@RestController
+@RequestMapping("/rest")
+public class RestTestController {
+
+    /**
+     * 验证拦截
+     * @return
+     */
+    @ApiOperation(value = "测试接口")
+    @RequestMapping(value = "/testAuth", method = RequestMethod.GET)
+    public String test() {
+        return "auth token";
+    }
+
+    /**
+     * 匿名调用:@RestAnon
+     * @return
+     */
+    @ApiOperation(value = "匿名访问接口")
+    @ApiImplicitParam(name = "token", value = "授权码")
+    @RequestMapping(value = "/testAnon", method = RequestMethod.GET)
+    @RestAnon
+    public String testAnon() {
+        return "rest anon";
+    }
+
+}

+ 47 - 0
src/main/java/net/chenlin/dp/modules/sys/controller/SysKaptchaController.java

@@ -0,0 +1,47 @@
+package net.chenlin.dp.modules.sys.controller;
+
+import com.google.code.kaptcha.Constants;
+import com.google.code.kaptcha.Producer;
+import net.chenlin.dp.common.utils.ShiroUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.imageio.ImageIO;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+/**
+ * 验证码请求
+ * @author zcl<yczclcn@163.com>
+ */
+@Controller
+public class SysKaptchaController {
+
+    @Autowired
+    private Producer producer;
+
+    /**
+     * 验证码
+     * @param response
+     * @throws IOException
+     */
+    @RequestMapping("/captcha.jpg")
+    public void captcha(HttpServletResponse response) throws IOException {
+        response.setHeader("Cache-Control", "no-store, no-cache");
+        response.setContentType("image/jpeg");
+
+        //生成文字验证码
+        String text = producer.createText();
+        //生成图片验证码
+        BufferedImage image = producer.createImage(text);
+        //保存到shiro session
+        ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
+
+        ServletOutputStream out = response.getOutputStream();
+        ImageIO.write(image, "jpg", out);
+    }
+
+}

+ 29 - 0
src/main/java/net/chenlin/dp/modules/sys/controller/SysLoginController.java

@@ -1,9 +1,12 @@
 package net.chenlin.dp.modules.sys.controller;
 
+import com.google.code.kaptcha.Constants;
 import net.chenlin.dp.common.annotation.SysLog;
+import net.chenlin.dp.common.support.properties.GlobalProperties;
 import net.chenlin.dp.common.utils.MD5Utils;
 import net.chenlin.dp.common.utils.ShiroUtils;
 import net.chenlin.dp.modules.sys.service.SysUserService;
+import org.apache.commons.lang.StringUtils;
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authc.*;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -23,6 +26,9 @@ public class SysLoginController extends AbstractController {
 	@Autowired
 	private SysUserService sysUserService;
 
+	@Autowired
+	private GlobalProperties globalProperties;
+
 	/**
 	 * 跳转登录页面
 	 * @return
@@ -44,6 +50,29 @@ public class SysLoginController extends AbstractController {
 		String username = getParam("username").trim();
 		String password = getParam("password").trim();
 		try {
+			// 开启验证码
+			if (globalProperties.isKaptchaEnable()) {
+				String code = getParam("code").trim();
+				if (StringUtils.isBlank(code)) {
+					model.addAttribute("errorMsg", "验证码不能为空");
+					return html("/login");
+				}
+				String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
+				if (!code.equalsIgnoreCase(kaptcha)) {
+					model.addAttribute("errorMsg", "验证码错误");
+					return html("/login");
+				}
+			}
+			// 用户名验证
+			if (StringUtils.isBlank(username)) {
+				model.addAttribute("errorMsg", "用户名不能为空");
+				return html("/login");
+			}
+			// 密码验证
+			if (StringUtils.isBlank(password)) {
+				model.addAttribute("errorMsg", "密码不能为空");
+				return html("/login");
+			}
 			UsernamePasswordToken token = new UsernamePasswordToken(username, MD5Utils.encrypt(username, password));
 			ShiroUtils.getSubject().login(token);
 			SecurityUtils.getSubject().getSession().setAttribute("sessionFlag", true);

+ 3 - 0
src/main/resources/application.yml

@@ -5,12 +5,15 @@ server:
     max-threads: 1000
     min-spare-threads: 30
   port: 8080
+  servlet:
+    context-path: /
 
 # 系统自定义全局配置,可根据实际需要扩展使用
 global:
   upload-location: /Users/zhouchenglin/dev/dp-boot/ #文件上传目录
   upload-mapping: /upload/ #文件上传目录访问路径
   redis-session-dao: false #是否使用使用redis会话管理器,true为开启,false为关闭
+  kaptcha-enable: true #是否开启验证码,true为开启,false为关闭
 
 spring:
   # 环境 dev:开发环境|test:测试环境|prod:生成环境

+ 1 - 1
src/main/webapp/WEB-INF/view/index.html

@@ -27,7 +27,7 @@
 					{{user.username}}</div>
 				<div class="navbar-custom-menu">
 					<ul class="nav navbar-nav">
-						<li><a href="/"><i
+						<li><a href="${ctxPath}"><i
 								class="fa fa-home"></i> &nbsp;首页</a></li>
 						<li><a href="http://dp-dev.mydoc.io/" target="_blank"><i
 								class="fa fa-paper-plane"></i> &nbsp;项目文档</a></li>

+ 24 - 2
src/main/webapp/WEB-INF/view/login.html

@@ -13,10 +13,15 @@
 		<div id="darkbannerwrap"></div>
 		<form action="${ctxPath}/login" method="post" id="loginForm">
             <h4 class="tip">${errorMsg!}</h4>
-			<input placeholder="用户名" id="username" name="username" type="text">
+			<input placeholder="用户名" id="username" name="username" type="text" class="loginInput">
 			<hr class="hr15">
-			<input placeholder="密码" id="password" name="password" type="password">
+			<input placeholder="密码" id="password" name="password" type="password" class="loginInput">
 			<hr class="hr15">
+			@if(kaptchaEnable){
+			<input placeholder="验证码" id="code" name="code" type="text" class="codeInput loginInput">
+			<img src="captcha.jpg" alt="验证码" id="imgCode" class="codeImg" title="单击图片刷新!">
+			<hr class="hr15">
+			@}
 			<input value="登录" style="width: 100%;" type="button" id="loginBtn">
 			<hr class="hr20">
             <p class="copyright">
@@ -33,6 +38,10 @@
             }
             $('#loginBtn').on('click', login);
 
+            @if(kaptchaEnable){
+            $('#imgCode').on('click', refreshCode);
+            @}
+
             $(document).keyup(function (event) {
 				if (event.keyCode === 13) {
 				    $('#loginBtn').trigger('click');
@@ -41,6 +50,12 @@
 
         });
 
+        @if(kaptchaEnable){
+        function refreshCode() {
+            $('#imgCode').attr('src', "captcha.jpg?t=" + $.now());
+		}
+		@}
+
         function login() {
             var username = $('#username').val();
             if (username === '') {
@@ -52,6 +67,13 @@
                 $('.tip').text('密码不能为空');
                 return false;
             }
+            @if(kaptchaEnable){
+			var code = $('#code').val();
+			if (code === '') {
+				$('.tip').text('验证码不能为空');
+				return false;
+			}
+            @}
             $('#loginForm').submit();
         }
 	</script>

+ 9 - 4
src/main/webapp/static/css/login.min.css

@@ -54,10 +54,7 @@ a.logo{
     position: relative;
 }
 
-input[type=text],
-input[type=file],
-input[type=password],
-input[type=email], select {
+.loginInput {
     border: 1px solid #DCDEE0;
     vertical-align: middle;
     border-radius: 3px;
@@ -122,4 +119,12 @@ hr.hr20 {
     font-size: 12px;
     color: #666;
     text-align: center;
+}
+
+.codeInput{
+    width:60%;border-top-right-radius: 0;border-bottom-right-radius: 0;float:left;
+}
+
+.codeImg{
+    width:40%;cursor:pointer;float:left;
 }

+ 1 - 1
src/main/webapp/static/js/index.js

@@ -114,7 +114,7 @@ var vm = new Vue({
 				btn : [ '确定', '取消' ],
 				btnAlign : 'c',
 				yes : function() {
-					toUrl('/logout');
+					toUrl('logout');
 				}
 			});
 		}

Some files were not shown because too many files changed in this diff