STS创建Spring Boot项目实战(Rest接口、数据库、用户认证、分布式Token JWT、Redis操作、日志和统一异常处理)

非常感谢  http://blog.csdn.net/he90227/article/details/53308222

 

单点登录原理技术学习,更多知识请访问https://www.itkc8.com

 

 

 

 

1.项目创建

 

 

1、新建工程

 

 

2、选择打包方式,这边可以选择为打包为Jar包,或者传统的打包为War包

 

3、选择开发过程中使用到的技术,这边我选择的是Rest Repositories

 

4、新建测试用Controller

 

    文件内容如下

 

[java] view plain copy

  1. package com.xiaofangtech.example;  
  2.   
  3. import org.springframework.web.bind.annotation.RequestMapping;  
  4. import org.springframework.web.bind.annotation.RestController;  
  5.   
  6. @RestController  
  7. public class HelloController {  
  8.     @RequestMapping("/greeting")  
  9.     public String hello()  
  10.     {  
  11.         return "Hello world";  
  12.     }  
  13. }  


5、以Srping Boot App 方式运行

 

 

正常运行后控制台如下

 

6、测试运行

至此,一个最简单的hello world的工程创建运行完成

 

7、打包部署

   7.1 打包为可运行jar包

   使用mvn package 进行打包

   

 

 然后run ,运行成功后如下生成jar包

 

 

 

 7.2 打包为传统的war包 

       当第2步中选择的打包方式为war时,执行7.1中mvn package时,生成的包就是war包

     

 

 

运行war包跟运行jar包一样,找到war包所在目录,注解运行war包

 

D:\new_tech\spring-suite-tool\workspace\workspace1\demo1\target>Java -jar demo1-0.0.1-SNAPSHOT.war

 

访问服务接口: localhost:8080/greeting

 

 

2.代码实现连接数据实现Rest接口和Basic 基础认证

0.引入pom依赖

 

[html] view plain copy

  1. <dependency>  
  2.             <groupId>org.projectlombok</groupId>  
  3.             <artifactId>lombok</artifactId>  
  4.         </dependency>  
  5.   
  6.         <!-- 使用MySQL数据库,并用Spring Data JPA来作为数据库访问 -->  
  7.         <dependency>  
  8.             <groupId>org.springframework.boot</groupId>  
  9.             <artifactId>spring-boot-starter-data-jpa</artifactId>  
  10.         </dependency>  
  11.         <dependency>  
  12.             <groupId>mysql</groupId>  
  13.             <artifactId>mysql-connector-java</artifactId>  
  14.         </dependency>  

 

 

 

1.代码整体结构

 

 

2.注册Filter过滤器的两种方式:

        1.在自定义的Filter上使用注解:

           

[java] view plain copy

  1. /* 
  2.  * Filter实现简单的Http Basic 认证  
  3.  */  
  4. @Component  
  5. @WebFilter(filterName = "httpBasicAuthorizedFilter", urlPatterns="/user/*")  
  6. public class HttpBasicAuthorizeFilter implements Filter {  

 

       2.在配置类中定义Filter

 

[java] view plain copy

  1. @Bean    
  2.     public FilterRegistrationBean  filterRegistrationBean() {    
  3.         FilterRegistrationBean registrationBean = new FilterRegistrationBean();    
  4.         HttpBasicAuthorizeFilter httpBasicFilter = new HttpBasicAuthorizeFilter();    
  5.         registrationBean.setFilter(httpBasicFilter);    
  6.         List<String> urlPatterns = new ArrayList<String>();    
  7.         urlPatterns.add("/user/*");    
  8.         registrationBean.setUrlPatterns(urlPatterns);    
  9.         return registrationBean;    
  10.     }    

 

3.数据库和Rest接口操作效果展示:

 

 

4.过滤器效果展示

  

代码中固定用户名密码都为test,所以对接口进行请求时,需要添加以下认证头信息

Authorization: Basic dGVzdDp0ZXN0

dGVzdDp0ZXN0 为 test:test 经过base64编码后的结果

 

如果未添加认证信息或者认证信息错误,返回没有权限的错误信息

 

 

当认证信息正确,返回请求结果

 

3.自定义Properties解析类和分布式Token JWT用户校验

    1.自定义Properties解析类的使用规则

                  1.定义Properties配置文件   ---- jwt.properties

                      

[html] view plain copy

  1. jwt.info.clientId=098f6bcd4621d373cade4e832627b4f6  
  2. jwt.info.base64Secret=MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY=  
  3. jwt.info.name=restapiuser  
  4. jwt.info.expiresSecond=172800  

 

 

                  2.自定义解析类                      ---- JwtInfo.java  指定配置文件地址和配置前缀,属性是前缀之后的名称

[java] view plain copy

  1. package com.jay.properties;  
  2.   
  3. import org.springframework.boot.context.properties.ConfigurationProperties;  
  4. /* 
  5.  * 自定义配置文件的解析类 
  6.  */  
  7. @ConfigurationProperties(prefix = "jwt.info", locations = "classpath:/config/jwt.properties")  
  8. public class JwtInfo {  
  9.     private String clientId;    
  10.     private String base64Secret;    
  11.     private String name;    
  12.     private int expiresSecond;  
  13.     public String getClientId() {  
  14.         return clientId;  
  15.     }  
  16.     public void setClientId(String clientId) {  
  17.         this.clientId = clientId;  
  18.     }  
  19.     public String getBase64Secret() {  
  20.         return base64Secret;  
  21.     }  
  22.     public void setBase64Secret(String base64Secret) {  
  23.         this.base64Secret = base64Secret;  
  24.     }  
  25.     public String getName() {  
  26.         return name;  
  27.     }  
  28.     public void setName(String name) {  
  29.         this.name = name;  
  30.     }  
  31.     public int getExpiresSecond() {  
  32.         return expiresSecond;  
  33.     }  
  34.     public void setExpiresSecond(int expiresSecond) {  
  35.         this.expiresSecond = expiresSecond;  
  36.     }  
  37.       
  38. }  

 

 

                  3.启动类或配置类中,指定自定义Properties解析类

[java] view plain copy

  1. package com.jay;  
  2.   
  3. import org.springframework.boot.SpringApplication;  
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;  
  5. import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties.Jwt;  
  6. import org.springframework.boot.context.properties.EnableConfigurationProperties;  
  7. import org.springframework.boot.web.servlet.ServletComponentScan;  
  8.   
  9. import com.jay.properties.JwtInfo;  
  10.   
  11. @SpringBootApplication  
  12. @EnableConfigurationProperties(JwtInfo.class)  //加载自定义的properties解析类  
  13. public class Demo1Application {  
  14.   
  15.     public static void main(String[] args) {  
  16.         SpringApplication.run(Demo1Application.class, args);  
  17.     }  
  18. }  

 

               4.输出配置文件信息         ---- JwtInfoController.java

 

[java] view plain copy

  1. package com.jay.controller;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.web.bind.annotation.RequestMapping;  
  5. import org.springframework.web.bind.annotation.RequestMethod;  
  6. import org.springframework.web.bind.annotation.RestController;  
  7.   
  8. import com.jay.properties.JwtInfo;  
  9. import com.jay.vo.ResultMsg;  
  10. import com.jay.vo.ResultStatusCode;  
  11.   
  12. @RestController  
  13. @RequestMapping("/jwt")  
  14. public class JwtInfoController {  
  15.   
  16.     @Autowired  
  17.     private JwtInfo jwtInfo;  
  18.   
  19.     @RequestMapping(value = "/info", method = RequestMethod.GET)  
  20.     public Object getJwtInfo() {  
  21.         return new ResultMsg<JwtInfo>(true, ResultStatusCode.OK.getErrorCode(), ResultStatusCode.OK.getErrorMsg(), jwtInfo);  
  22.     }  
  23. }  


               5.效果展示

              


 

          2.使用分布式token  JWT进行用户认证

 

              

jwt(json web token)

用户发送按照约定,向服务端发送 Header、Payload 和 Signature,并包含认证信息(密码),验证通过后服务端返回一个token,之后用户使用该token作为登录凭证,适合于移动端和api

 

jwt使用流程

 

              1.添加 JWT依赖

[html] view plain copy

  1. <!-- JWT Json Web Token 依赖 -->  
  2.         <dependency>  
  3.             <groupId>io.jsonwebtoken</groupId>  
  4.             <artifactId>jjwt</artifactId>  
  5.             <version>0.7.0</version>  
  6.         </dependency>  


             2.编写Jwt解析类和Jwt过滤器

             

[java] view plain copy

  1. package com.jay.util.jwt;  
  2.   
  3. import java.security.Key;  
  4. import java.util.Date;  
  5.   
  6. import javax.crypto.spec.SecretKeySpec;  
  7. import javax.xml.bind.DatatypeConverter;  
  8.   
  9. import io.jsonwebtoken.Claims;  
  10. import io.jsonwebtoken.JwtBuilder;  
  11. import io.jsonwebtoken.Jwts;  
  12. import io.jsonwebtoken.SignatureAlgorithm;  
  13.   
  14. /* 
  15.  * 构造及解析jwt的工具类 
  16.  */  
  17. public class JwtHelper {  
  18.     public static Claims parseJWT(String jsonWebToken, String base64Security){  
  19.         try  
  20.         {  
  21.             Claims claims = Jwts.parser()  
  22.                        .setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))  
  23.                        .parseClaimsJws(jsonWebToken).getBody();  
  24.             return claims;  
  25.         }  
  26.         catch(Exception ex)  
  27.         {  
  28.             return null;  
  29.         }  
  30.     }  
  31.       
  32.     /** 
  33.      * 生成token 
  34.      *  
  35.      * @author hetiewei 
  36.      * @date 2016年10月18日 下午2:51:38 
  37.      * @param name     keyId 
  38.      * @param userId    
  39.      * @param role 
  40.      * @param audience   接收者 
  41.      * @param issuer     发行者 
  42.      * @param TTLMillis  过期时间(毫秒) 
  43.      * @param base64Security 
  44.      * @return 
  45.      */  
  46.     public static String createJWT(String name, String userId, String role,   
  47.             String audience, String issuer, long TTLMillis, String base64Security)   
  48.     {  
  49.         SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;  
  50.            
  51.         long nowMillis = System.currentTimeMillis();  
  52.         Date now = new Date(nowMillis);  
  53.            
  54.         //生成签名密钥  
  55.         byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security);  
  56.         Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());  
  57.            
  58.           //添加构成JWT的参数  
  59.         JwtBuilder builder = Jwts.builder().setHeaderParam("typ""JWT")  
  60.                                         .claim("role", role)  
  61.                                         .claim("unique_name", name)  
  62.                                         .claim("userid", userId)  
  63.                                         .setIssuer(issuer)  
  64.                                         .setAudience(audience)  
  65.                                         .signWith(signatureAlgorithm, signingKey);  
  66.          //添加Token过期时间  
  67.         if (TTLMillis >= 0) {  
  68.             long expMillis = nowMillis + TTLMillis;  
  69.             Date exp = new Date(expMillis);  
  70.             builder.setExpiration(exp).setNotBefore(now);  
  71.         }  
  72.            
  73.          //生成JWT  
  74.         return builder.compact();  
  75.     }   
  76. }  

 

 

 

[java] view plain copy

  1. package com.jay.filter;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import javax.servlet.Filter;  
  6. import javax.servlet.FilterChain;  
  7. import javax.servlet.FilterConfig;  
  8. import javax.servlet.ServletException;  
  9. import javax.servlet.ServletRequest;  
  10. import javax.servlet.ServletResponse;  
  11. import javax.servlet.http.HttpServletRequest;  
  12. import javax.servlet.http.HttpServletResponse;  
  13.   
  14. import org.springframework.beans.factory.annotation.Autowired;  
  15. import org.springframework.web.context.support.SpringBeanAutowiringSupport;  
  16.   
  17. import com.fasterxml.jackson.databind.ObjectMapper;  
  18. import com.jay.properties.JwtInfo;  
  19. import com.jay.util.jwt.JwtHelper;  
  20. import com.jay.vo.ResultMsg;  
  21. import com.jay.vo.ResultStatusCode;  
  22.   
  23. /* 
  24.  * 用于JWT认证的过滤器 
  25.  */  
  26. public class JwtAuthorizeFilter implements Filter{  
  27.       
  28.     /* 
  29.      * 注入配置文件类 
  30.      */  
  31.     @Autowired  
  32.     private JwtInfo jwtInfo;  
  33.   
  34.     @Override  
  35.     public void destroy() {  
  36.           
  37.     }  
  38.   
  39.     @Override  
  40.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  
  41.             throws IOException, ServletException {  
  42.          ResultMsg<Object> resultMsg;    
  43.             HttpServletRequest httpRequest = (HttpServletRequest)request;    
  44.             String auth = httpRequest.getHeader("Authorization");    
  45.             if ((auth != null) && (auth.length() > 7))    
  46.             {    
  47.                 String HeadStr = auth.substring(06).toLowerCase();    
  48.                 if (HeadStr.compareTo("bearer") == 0)    
  49.                 {    
  50.                         
  51.                     auth = auth.substring(7, auth.length());     
  52.                     if (JwtHelper.parseJWT(auth, jwtInfo.getBase64Secret()) != null)    
  53.                     {    
  54.                         chain.doFilter(request, response);    
  55.                         return;    
  56.                     }    
  57.                 }    
  58.             }    
  59.               
  60.             //验证不通过  
  61.             HttpServletResponse httpResponse = (HttpServletResponse) response;    
  62.             httpResponse.setCharacterEncoding("UTF-8");      
  63.             httpResponse.setContentType("application/json; charset=utf-8");     
  64.             httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);    
  65.         
  66.             //将验证不通过的错误返回  
  67.             ObjectMapper mapper = new ObjectMapper();    
  68.                 
  69.             resultMsg = new ResultMsg<Object>(true, ResultStatusCode.INVALID_TOKEN.getErrorCode(), ResultStatusCode.INVALID_TOKEN.getErrorMsg(), null);    
  70.             httpResponse.getWriter().write(mapper.writeValueAsString(resultMsg));    
  71.             return;    
  72.     }  
  73.   
  74.     @Override  
  75.     public void init(FilterConfig filterConfig) throws ServletException {  
  76.         SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());  
  77.     }  
  78.   
  79. }  

 

 

 

 

 

 

            3.在Jwt配置类中,添加过滤器

             

[java] view plain copy

  1. package com.jay.config;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import org.springframework.boot.context.embedded.FilterRegistrationBean;  
  7. import org.springframework.context.annotation.Bean;  
  8. import org.springframework.context.annotation.Configuration;  
  9.   
  10. import com.jay.filter.JwtAuthorizeFilter;  
  11.   
  12.   
  13. /* 
  14.  * 注册jwt认证过滤器 
  15.  */  
  16. @Configuration  
  17. public class JwtConfig {  
  18.       
  19.     /* 
  20.      * 注册过滤器类和过滤的url 
  21.      */  
  22.     @Bean  
  23.     public FilterRegistrationBean basicFilterRegistrationBean(){  
  24.         FilterRegistrationBean registrationBean = new FilterRegistrationBean();  
  25.         JwtAuthorizeFilter filter = new JwtAuthorizeFilter();  
  26.         registrationBean.setFilter(filter);  
  27.           
  28.         List<String> urlPatterns = new ArrayList<>();  
  29.         urlPatterns.add("/user/*");  
  30.           
  31.         registrationBean.setUrlPatterns(urlPatterns);  
  32.         return registrationBean;  
  33.     }  
  34. }  

 

            

            4.效果展示:

                       1. 获取token,传入用户认证信息

 

 

 

 

                    2.使用上面获取的token进行接口调用,     未使用token,获取token错误,或者token过期时

 

 

               3.使用正确的token时

                   

                    

 

 

 

特别注意:

              JWT使用时,可以通过Cookie机制,自动的传递!!!

 

 

4.Redis + Cookie 机制,进行验证码的校验

 

1.添加redis和captcha库依赖

 

[html] view plain copy

  1. <!-- 整合redis -->  
  2.          <dependency>    
  3.             <groupId>org.springframework.boot</groupId>    
  4.             <artifactId>spring-boot-starter-redis</artifactId>    
  5.         </dependency>    
  6.         <!-- 第三方验证码库 -->  
  7.         <dependency>  
  8.             <groupId>cn.apiclub.tool</groupId>  
  9.             <artifactId>simplecaptcha</artifactId>  
  10.             <version>1.2.2</version>  
  11.         </dependency>  

 

2.redis配置

 

[html] view plain copy

  1. ##Redis配置  
  2. spring.redis.database=1  
  3. spring.redis.host=localhost  
  4. #spring.redis.password=password  
  5. spring.redis.port=6379  
  6. spring.redis.timeout=2000  
  7. spring.redis.pool.max-idle=8  
  8. spring.redis.pool.min-idle=0  
  9. spring.redis.pool.max-active=8  
  10. spring.redis.pool.max-wait=-1  

 

3.Redis配置类,实例化Redis模板

 

[java] view plain copy

  1. package com.jay.config;  
  2.   
  3. import org.springframework.context.annotation.Bean;  
  4. import org.springframework.context.annotation.Configuration;  
  5. import org.springframework.data.redis.connection.RedisConnectionFactory;  
  6. import org.springframework.data.redis.core.RedisTemplate;  
  7. import org.springframework.data.redis.core.StringRedisTemplate;  
  8. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;  
  9.   
  10. import com.fasterxml.jackson.annotation.JsonAutoDetect;  
  11. import com.fasterxml.jackson.annotation.PropertyAccessor;  
  12. import com.fasterxml.jackson.databind.ObjectMapper;  
  13.   
  14. @Configuration  
  15. public class RedisConfig {  
  16.       
  17.     // 定义Redis模板  
  18.     @Bean  
  19.     public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {  
  20.         StringRedisTemplate template = new StringRedisTemplate(factory);  
  21.         // 设置序列化工具, 这样缓存的Bean就不需要再试下Serializable接口  
  22.         setSerrializer(template);  
  23.         template.afterPropertiesSet();  
  24.         return template;  
  25.     }  
  26.   
  27.     // 设置序列化  
  28.     private void setSerrializer(StringRedisTemplate template) {  
  29.         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);  
  30.         ObjectMapper om = new ObjectMapper();  
  31.         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);  
  32.         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  
  33.         jackson2JsonRedisSerializer.setObjectMapper(om);  
  34.         template.setValueSerializer(jackson2JsonRedisSerializer);  
  35.     }  
  36. }  

 

4.Controller层操作代码

 

[java] view plain copy

  1. package com.jay.controller;  
  2.   
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.IOException;  
  5. import java.util.UUID;  
  6. import java.util.concurrent.TimeUnit;  
  7.   
  8. import javax.imageio.ImageIO;  
  9. import javax.servlet.http.Cookie;  
  10. import javax.servlet.http.HttpServletRequest;  
  11. import javax.servlet.http.HttpServletResponse;  
  12.   
  13. import org.springframework.beans.factory.annotation.Autowired;  
  14. import org.springframework.data.redis.core.RedisTemplate;  
  15. import org.springframework.http.HttpRequest;  
  16. import org.springframework.http.MediaType;  
  17. import org.springframework.stereotype.Controller;  
  18. import org.springframework.web.bind.annotation.PathVariable;  
  19. import org.springframework.web.bind.annotation.RequestMapping;  
  20. import org.springframework.web.bind.annotation.RequestMethod;  
  21. import org.springframework.web.bind.annotation.ResponseBody;  
  22.   
  23. import com.jay.util.CookieUtils;  
  24. import com.jay.vo.ResultMsg;  
  25. import com.jay.vo.ResultStatusCode;  
  26.   
  27. import cn.apiclub.captcha.Captcha;  
  28. import cn.apiclub.captcha.backgrounds.GradiatedBackgroundProducer;  
  29. import cn.apiclub.captcha.gimpy.FishEyeGimpyRenderer;  
  30. import io.swagger.annotations.ApiOperation;  
  31.   
  32. @Controller  
  33. @RequestMapping("/redis")  
  34. public class RedisCaptchaController {  
  35.   
  36.     @Autowired  
  37.     private RedisTemplate<String, String> redisTemplate;  
  38.   
  39.     private static int captchaExpires = 3 * 60// 超时时间3min,验证码超时,自动冲redis中删除  
  40.     private static int captchaW = 200;  
  41.     private static int captchaH = 60;  
  42.     private static String cookieName = "CaptchaCode";  
  43.   
  44.     @RequestMapping(value = "getcaptcha", method = RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)  
  45.     public @ResponseBody byte[] getCaptcha(HttpServletResponse response) {  
  46.         // 生成验证码  
  47.         String uuid = UUID.randomUUID().toString();  
  48.         Captcha captcha = new Captcha.Builder(captchaW, captchaH).addText()  
  49.                 .addBackground(new GradiatedBackgroundProducer()).gimp(new FishEyeGimpyRenderer()).build();  
  50.   
  51.         // 将验证码以<key,value>形式缓存到redis  
  52.         redisTemplate.opsForValue().set(uuid, captcha.getAnswer(), captchaExpires, TimeUnit.SECONDS);  
  53.   
  54.         // 将验证码key,及验证码的图片返回  
  55.         Cookie cookie = new Cookie(cookieName, uuid);  
  56.         response.addCookie(cookie);  
  57.         ByteArrayOutputStream bao = new ByteArrayOutputStream();  
  58.         try {  
  59.             ImageIO.write(captcha.getImage(), "png", bao);  
  60.             return bao.toByteArray();  
  61.         } catch (IOException e) {  
  62.             return null;  
  63.         }  
  64.     }  
  65.       
  66.     /* 
  67.      * 说明: 
  68.      *    1.captchaCode来自客户端的Cookie,在访问时,通过服务端设置 
  69.      *    2.captcha是用户填写的验证码,将用户填写的验证码和通过captchaCode从redis中获取的验证码进行对比即可 
  70.      *  
  71.      */  
  72.     @ApiOperation(value = "验证码校验")  
  73.     @RequestMapping(value = "/captcha/check/{captcha}")  
  74.     @ResponseBody  
  75.     public ResultMsg<Object> checkCaptcha(@PathVariable("captcha") String captcha, HttpServletRequest request){  
  76.           
  77.         String captchaCode = CookieUtils.getCookie(request, cookieName);  
  78.           
  79.         ResultMsg<Object> result;  
  80.           
  81.         try{  
  82.         if (captcha == null)    
  83.         {    
  84.             throw new Exception();    
  85.         }    
  86.           
  87.         //redis中查询验证码  
  88.         String captchaValue = redisTemplate.opsForValue().get(captchaCode);  
  89.           
  90.         if (captchaValue == null) {  
  91.             throw new Exception();  
  92.         }  
  93.           
  94.         if (captchaValue.compareToIgnoreCase(captcha) != 0) {  
  95.             throw new Exception();  
  96.         }  
  97.           
  98.         //验证码匹配成功,redis则删除对应的验证码  
  99.         redisTemplate.delete(captchaCode);  
  100.                   
  101.         return new ResultMsg<Object>(true, ResultStatusCode.OK.getErrorCode(), ResultStatusCode.OK.getErrorMsg(), null);  
  102.   
  103.         }catch (Exception e) {  
  104.             result = new ResultMsg<Object>(false, ResultStatusCode.INVALID_CAPTCHA.getErrorCode(), ResultStatusCode.INVALID_CAPTCHA.getErrorMsg(), null);  
  105.         }  
  106.         return result;  
  107.     }  
  108.       
  109.   
  110. }  

 

5.效果展示

 

1.访问生成验证码

 

 

2.验证验证码

 

 

项目源码下载:下载地址

单点登录原理技术学习,更多知识请访问https://www.itkc8.com

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值