作者 朱兆平

add:

用户登录认证采用SM3加密方式.
新增用户与更新用户接口还没改为SM3方式
@@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
10 <relativePath/> <!-- lookup parent from repository --> 10 <relativePath/> <!-- lookup parent from repository -->
11 </parent> 11 </parent>
12 <groupId>com.tianbo</groupId> 12 <groupId>com.tianbo</groupId>
13 - <artifactId>warehouse</artifactId> 13 + <artifactId>cloud-user-center</artifactId>
14 <version>5.0Beta-DataPermission</version> 14 <version>5.0Beta-DataPermission</version>
15 <name>usercenter</name> 15 <name>usercenter</name>
16 <description>usercenter for springcloud</description> 16 <description>usercenter for springcloud</description>
@@ -21,6 +21,8 @@ @@ -21,6 +21,8 @@
21 <fastjson_version>1.2.28</fastjson_version> 21 <fastjson_version>1.2.28</fastjson_version>
22 <lombok_sersion>1.18.6</lombok_sersion> 22 <lombok_sersion>1.18.6</lombok_sersion>
23 <swagger2_version>2.9.2</swagger2_version> 23 <swagger2_version>2.9.2</swagger2_version>
  24 + <shiro.version>1.2.5</shiro.version>
  25 + <commons-lang3.version>3.3.2</commons-lang3.version>
24 </properties> 26 </properties>
25 27
26 <dependencies> 28 <dependencies>
@@ -220,6 +222,26 @@ @@ -220,6 +222,26 @@
220 <artifactId>spring-boot-starter-actuator</artifactId> 222 <artifactId>spring-boot-starter-actuator</artifactId>
221 </dependency> 223 </dependency>
222 224
  225 + <!-- 兼容卡口登录插件需要用shiro-->
  226 + <dependency>
  227 + <groupId>org.apache.shiro</groupId>
  228 + <artifactId>shiro-core</artifactId>
  229 + <version>${shiro.version}</version>
  230 + </dependency>
  231 +
  232 + <dependency>
  233 + <groupId>org.apache.commons</groupId>
  234 + <artifactId>commons-lang3</artifactId>
  235 + <version>${commons-lang3.version}</version>
  236 + </dependency>
  237 +
  238 + <!-- 验证码配置-->
  239 + <dependency>
  240 + <groupId>com.github.axet</groupId>
  241 + <artifactId>kaptcha</artifactId>
  242 + <version>0.0.9</version>
  243 + </dependency>
  244 +
223 </dependencies> 245 </dependencies>
224 246
225 <dependencyManagement> 247 <dependencyManagement>
  1 +package com.tianbo.warehouse.bean;
  2 +
  3 +import com.google.code.kaptcha.impl.DefaultKaptcha;
  4 +import com.google.code.kaptcha.util.Config;
  5 +import org.springframework.context.annotation.Bean;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +import java.util.Properties;
  9 +
  10 +@Configuration
  11 +public class KaptchaConfig {
  12 +
  13 + /*声明验证码生成策略属性 Bean*/
  14 + @Bean
  15 + public DefaultKaptcha captchaProducer(){
  16 + DefaultKaptcha captchaProducer =new DefaultKaptcha();
  17 + Properties properties =new Properties();
  18 + properties.setProperty("kaptcha.border","yes");
  19 + properties.setProperty("kaptcha.border.color","105,179,90");
  20 + properties.setProperty("kaptcha.textproducer.font.color","red");
  21 + properties.setProperty("kaptcha.image.width","125");
  22 + properties.setProperty("kaptcha.image.height","60");
  23 + properties.setProperty("kaptcha.textproducer.font.size","36");
  24 + properties.setProperty("kaptcha.session.key","code");
  25 + properties.setProperty("kaptcha.textproducer.char.length","4");
  26 + properties.setProperty("kaptcha.textproducer.font.names","宋体,楷体,微软雅黑");
  27 + Config config=new Config(properties);
  28 + captchaProducer.setConfig(config);
  29 + return captchaProducer;
  30 + }
  31 +}
  1 +package com.tianbo.warehouse.controller;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +
  5 +import com.google.code.kaptcha.impl.DefaultKaptcha;
  6 +
  7 +import com.thoughtworks.xstream.core.util.Base64Encoder;
  8 +import com.tianbo.warehouse.controller.response.ResultJson;
  9 +import com.tianbo.warehouse.model.ROLE;
  10 +import com.tianbo.warehouse.service.RoleService;
  11 +
  12 +import com.tianbo.warehouse.util.RedisUtils;
  13 +
  14 +import lombok.extern.slf4j.Slf4j;
  15 +import org.springframework.beans.factory.annotation.Autowired;
  16 +import org.springframework.web.bind.annotation.PostMapping;
  17 +import org.springframework.web.bind.annotation.RequestMapping;
  18 +import org.springframework.web.bind.annotation.RestController;
  19 +
  20 +import javax.imageio.ImageIO;
  21 +
  22 +import java.awt.image.BufferedImage;
  23 +import java.io.ByteArrayOutputStream;
  24 +import java.io.IOException;
  25 +import java.util.*;
  26 +
  27 +@Slf4j
  28 +@RestController()
  29 +@RequestMapping("/anonymous")
  30 +public class AnonymousController {
  31 +
  32 + @Autowired
  33 + RoleService roleService;
  34 +
  35 + @Autowired
  36 + RedisUtils redisUtils;
  37 +
  38 + @Autowired
  39 + private DefaultKaptcha captchaProducer;
  40 +
  41 + /**
  42 + * 配置匿名者可以访问的路由,并更新到redis,匿名者默认可以访问的role_name =ROLE_anonymous
  43 + * 此方法会将所有符合权限组名=ROLE_anonymous的权限更新到redis中,供gateway调用判断权限
  44 + * @return
  45 + */
  46 + @PostMapping("initAnonymousRoute")
  47 + public ResultJson initAnonymousRoute(){
  48 + List<ROLE> list = roleService.getROLE_anonymousPermList();
  49 + String json = JSON.toJSONString(list);
  50 + boolean result= redisUtils.set("ROLE_anonymous_routers", json,0);
  51 + return result ? new ResultJson("200","匿名者权限配置成功") :new ResultJson("500","匿名者权限配置失败");
  52 + }
  53 +
  54 + /**
  55 + * 生成验证码
  56 + */
  57 + @RequestMapping(value = "/randCode")
  58 + public ResultJson getRandCode(){
  59 + // 生成自定义验证码文字
  60 + Random random = new Random();
  61 + int num1 = random.nextInt(100);
  62 + int num2 = random.nextInt(100);
  63 + int sum = num1 + num2;
  64 + System.out.println("验证码答案为: = " + sum);
  65 + String capText = num1 + " + " + num2 + " = ";
  66 + // 获取验证码上的文字
  67 + // String capText = captchaProducer.createText();
  68 + // 将文件渲染到图片上
  69 + BufferedImage bi = captchaProducer.createImage(capText);
  70 + ByteArrayOutputStream outputStream = null;
  71 + outputStream = new ByteArrayOutputStream();
  72 + Base64Encoder encoder = new Base64Encoder();
  73 + Map<String,Object> map = new HashMap<>();
  74 + String verifyToken = "";
  75 + try {
  76 + verifyToken = UUID.randomUUID().toString();
  77 + redisUtils.set("verifyToken:" + verifyToken,String.valueOf(sum),1200);
  78 + ImageIO.write(bi, "jpeg", outputStream);
  79 + map.put("verifyImg","data:image/jpeg;base64,"+encoder.encode(outputStream.toByteArray()));
  80 + } catch (IOException e) {
  81 + e.printStackTrace();
  82 + return new ResultJson("500","verify get error");
  83 + }
  84 + return new ResultJson("200","verify get ok",map,verifyToken);
  85 +
  86 + }
  87 +}
@@ -40,4 +40,11 @@ public class ResultJson<T> implements Serializable{ @@ -40,4 +40,11 @@ public class ResultJson<T> implements Serializable{
40 this.msg = msg; 40 this.msg = msg;
41 this.data = data; 41 this.data = data;
42 } 42 }
  43 +
  44 + public ResultJson(String code, String msg, T data,String jwtToken) {
  45 + this.code = code;
  46 + this.msg = msg;
  47 + this.data = data;
  48 + this.jwtToken = jwtToken;
  49 + }
43 } 50 }
  1 +package com.tianbo.warehouse.security.config;
  2 +
  3 +import com.tianbo.warehouse.security.login.SM3EncryptUtil;
  4 +import org.springframework.security.crypto.password.PasswordEncoder;
  5 +import org.springframework.stereotype.Service;
  6 +
  7 +@Service(value = "sm3EncodeService")
  8 +public class SM3PasswordEncoder implements PasswordEncoder {
  9 +
  10 + @Override
  11 + public String encode(CharSequence charSequence) {
  12 + String enncodePassword = SM3EncryptUtil.passwordSm3(charSequence.toString());
  13 + return enncodePassword;
  14 + }
  15 +
  16 + @Override
  17 + public boolean matches(CharSequence rawPassword, String encodedPassword) {
  18 + //用户输入的登录密码
  19 + String endcodeInputPass = SM3EncryptUtil.passwordSm3(rawPassword.toString());
  20 + return encodedPassword.equals(endcodeInputPass);
  21 + }
  22 +}
1 package com.tianbo.warehouse.security.config; 1 package com.tianbo.warehouse.security.config;
2 2
3 -import com.netflix.discovery.converters.Auto;  
4 import com.tianbo.warehouse.security.CustomUserDetailService; 3 import com.tianbo.warehouse.security.CustomUserDetailService;
5 import com.tianbo.warehouse.security.filter.JwtAuthenticationTokenFilter; 4 import com.tianbo.warehouse.security.filter.JwtAuthenticationTokenFilter;
6 import com.tianbo.warehouse.security.handel.*; 5 import com.tianbo.warehouse.security.handel.*;
7 import com.tianbo.warehouse.security.MyFilterSecurityInterceptor; 6 import com.tianbo.warehouse.security.MyFilterSecurityInterceptor;
  7 +import com.tianbo.warehouse.security.login.MyLoginAuthenticationProcessFilter;
8 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.beans.factory.annotation.Autowired;
9 import org.springframework.beans.factory.annotation.Qualifier; 9 import org.springframework.beans.factory.annotation.Qualifier;
10 -import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;  
11 import org.springframework.boot.autoconfigure.security.servlet.PathRequest; 10 import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
12 import org.springframework.context.annotation.Configuration; 11 import org.springframework.context.annotation.Configuration;
13 import org.springframework.core.annotation.Order; 12 import org.springframework.core.annotation.Order;
14 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 13 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
15 -import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;  
16 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 14 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
17 import org.springframework.security.config.annotation.web.builders.HttpSecurity; 15 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
18 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 16 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
19 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 17 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
20 import org.springframework.security.config.http.SessionCreationPolicy; 18 import org.springframework.security.config.http.SessionCreationPolicy;
21 -import org.springframework.security.core.userdetails.UserDetailsService;  
22 -import org.springframework.security.crypto.password.PasswordEncoder;  
23 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; 19 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
24 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 20 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
25 import org.springframework.web.cors.CorsUtils; 21 import org.springframework.web.cors.CorsUtils;
@@ -34,7 +30,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -34,7 +30,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
34 private MyFilterSecurityInterceptor myFilterSecurityInterceptor; 30 private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
35 31
36 @Autowired 32 @Autowired
37 - private PasswordEncoder passwordEncoder; 33 + private PasswordEncoderImp passwordEncoder;
38 34
39 @Autowired 35 @Autowired
40 private MyAuthenticationSuccessHandler successHandler; 36 private MyAuthenticationSuccessHandler successHandler;
@@ -58,6 +54,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -58,6 +54,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
58 @Autowired 54 @Autowired
59 private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; 55 private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
60 56
  57 + private final MyLoginAuthenticationProcessFilter adminAuthenticationProcessingFilter;
  58 +
61 @Override 59 @Override
62 protected void configure(AuthenticationManagerBuilder auth) throws Exception { 60 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
63 //user Details Service验证 61 //user Details Service验证
@@ -66,6 +64,16 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -66,6 +64,16 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
66 auth.eraseCredentials(false); 64 auth.eraseCredentials(false);
67 } 65 }
68 66
  67 +
  68 + /**
  69 + * 用户密码校验过滤器
  70 + */
  71 +
  72 +
  73 + public WebSecurityConfig(MyLoginAuthenticationProcessFilter adminAuthenticationProcessingFilter) {
  74 + this.adminAuthenticationProcessingFilter = adminAuthenticationProcessingFilter;
  75 + }
  76 +
69 /** 77 /**
70 * 在configure(HttpSecurity http)方法中, 78 * 在configure(HttpSecurity http)方法中,
71 * 通过withObjectPostProcessor将刚刚创建的UrlFilterInvocationSecurityMetadataSource和UrlAccessDecisionManager注入进来。 79 * 通过withObjectPostProcessor将刚刚创建的UrlFilterInvocationSecurityMetadataSource和UrlAccessDecisionManager注入进来。
@@ -82,7 +90,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -82,7 +90,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
82 //跨域配置 90 //跨域配置
83 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() 91 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
84 //管理页面只允许管理员角色访问 92 //管理页面只允许管理员角色访问
85 - .antMatchers("/admin/**","/ROLE/**","/user/**").authenticated() 93 + .antMatchers("/admin/**","/ROLE/**","/user/**","/perm/**","/role/**").authenticated()
86 //任何请求,登录后可以访问 94 //任何请求,登录后可以访问
87 //其余的不需要验证 95 //其余的不需要验证
88 .anyRequest().permitAll() 96 .anyRequest().permitAll()
@@ -130,6 +138,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -130,6 +138,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
130 //关闭session 138 //关闭session
131 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 139 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
132 140
  141 + // 自定义过滤器认证用户名密码
  142 + http.addFilterAt(adminAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter.class);
133 143
134 //session管理 144 //session管理
135 //session失效后跳转 145 //session失效后跳转
  1 +package com.tianbo.warehouse.security.login;
  2 +
  3 +import org.apache.commons.codec.DecoderException;
  4 +import org.apache.commons.codec.binary.Base64;
  5 +import org.apache.commons.codec.binary.Hex;
  6 +
  7 +import java.io.UnsupportedEncodingException;
  8 +
  9 +/**
  10 + * 封装各种格式的编码解码工具类.
  11 + * 1.Commons-Codec的 hex/base64 编码
  12 + * 2.自制的base62 编码
  13 + * 3.Commons-Lang的xml/html escape
  14 + * 4.JDK提供的URLEncoder
  15 + * @author calvin
  16 + * @version 2013-01-15
  17 + */
  18 +public class Encodes {
  19 +
  20 + private static final String DEFAULT_URL_ENCODING = "UTF-8";
  21 + private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
  22 +
  23 + /**
  24 + * Hex编码.
  25 + */
  26 + public static String encodeHex(byte[] input) {
  27 + return new String(Hex.encodeHex(input));
  28 + }
  29 +
  30 + /**
  31 + * Hex解码.
  32 + */
  33 + public static byte[] decodeHex(String input) {
  34 + try {
  35 + return Hex.decodeHex(input.toCharArray());
  36 + } catch (DecoderException e) {
  37 + throw Exceptions.unchecked(e);
  38 + }
  39 + }
  40 +
  41 + /**
  42 + * Base64编码.
  43 + */
  44 + public static String encodeBase64(byte[] input) {
  45 + return new String(Base64.encodeBase64(input));
  46 + }
  47 +
  48 + /**
  49 + * Base64编码.
  50 + */
  51 + public static String encodeBase64(String input) {
  52 + try {
  53 + return new String(Base64.encodeBase64(input.getBytes(DEFAULT_URL_ENCODING)));
  54 + } catch (UnsupportedEncodingException e) {
  55 + return "";
  56 + }
  57 + }
  58 +
  59 +// /**
  60 +// * Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548).
  61 +// */
  62 +// public static String encodeUrlSafeBase64(byte[] input) {
  63 +// return Base64.encodeBase64URLSafe(input);
  64 +// }
  65 +
  66 + /**
  67 + * Base64解码.
  68 + */
  69 + public static byte[] decodeBase64(String input) {
  70 + return Base64.decodeBase64(input.getBytes());
  71 + }
  72 +
  73 + /**
  74 + * Base64解码.
  75 + */
  76 + public static String decodeBase64String(String input) {
  77 + try {
  78 + return new String(Base64.decodeBase64(input.getBytes()), DEFAULT_URL_ENCODING);
  79 + } catch (UnsupportedEncodingException e) {
  80 + return "";
  81 + }
  82 + }
  83 +
  84 + /**
  85 + * Base62编码。
  86 + */
  87 + public static String encodeBase62(byte[] input) {
  88 + char[] chars = new char[input.length];
  89 + for (int i = 0; i < input.length; i++) {
  90 + chars[i] = BASE62[((input[i] & 0xFF) % BASE62.length)];
  91 + }
  92 + return new String(chars);
  93 + }
  94 +
  95 +}
  1 +package com.tianbo.warehouse.security.login;
  2 +
  3 +import javax.servlet.http.HttpServletRequest;
  4 +import java.io.PrintWriter;
  5 +import java.io.StringWriter;
  6 +
  7 +/**
  8 + * 关于异常的工具类.
  9 + * @author calvin
  10 + * @version 2013-01-15
  11 + */
  12 +public class Exceptions {
  13 +
  14 + /**
  15 + * 将CheckedException转换为UncheckedException.
  16 + */
  17 + public static RuntimeException unchecked(Exception e) {
  18 + if (e instanceof RuntimeException) {
  19 + return (RuntimeException) e;
  20 + } else {
  21 + return new RuntimeException(e);
  22 + }
  23 + }
  24 +
  25 + /**
  26 + * 将ErrorStack转化为String.
  27 + */
  28 + public static String getStackTraceAsString(Throwable e) {
  29 + if (e == null){
  30 + return "";
  31 + }
  32 + StringWriter stringWriter = new StringWriter();
  33 + e.printStackTrace(new PrintWriter(stringWriter));
  34 + return stringWriter.toString();
  35 + }
  36 +
  37 + /**
  38 + * 判断异常是否由某些底层的异常引起.
  39 + */
  40 + public static boolean isCausedBy(Exception ex, Class<? extends Exception>... causeExceptionClasses) {
  41 + Throwable cause = ex.getCause();
  42 + while (cause != null) {
  43 + for (Class<? extends Exception> causeClass : causeExceptionClasses) {
  44 + if (causeClass.isInstance(cause)) {
  45 + return true;
  46 + }
  47 + }
  48 + cause = cause.getCause();
  49 + }
  50 + return false;
  51 + }
  52 +
  53 + /**
  54 + * 在request中获取异常类
  55 + * @param request
  56 + * @return
  57 + */
  58 + public static Throwable getThrowable(HttpServletRequest request){
  59 + Throwable ex = null;
  60 + if (request.getAttribute("exception") != null) {
  61 + ex = (Throwable) request.getAttribute("exception");
  62 + } else if (request.getAttribute("javax.servlet.error.exception") != null) {
  63 + ex = (Throwable) request.getAttribute("javax.servlet.error.exception");
  64 + }
  65 + return ex;
  66 + }
  67 +
  68 +}
  1 +package com.tianbo.warehouse.security.login;
  2 +
  3 +import org.springframework.security.authentication.AuthenticationManager;
  4 +import org.springframework.security.authentication.ProviderNotFoundException;
  5 +import org.springframework.security.core.Authentication;
  6 +import org.springframework.security.core.AuthenticationException;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import java.util.Objects;
  10 +
  11 +/**
  12 + * 自定义认证管理器
  13 + */
  14 +@Component
  15 +public class MyAuthenticationManager implements AuthenticationManager {
  16 + private final MyLoginAuthenticationProvider adminAuthenticationProvider;
  17 +
  18 + public MyAuthenticationManager(MyLoginAuthenticationProvider adminAuthenticationProvider) {
  19 + this.adminAuthenticationProvider = adminAuthenticationProvider;
  20 + }
  21 +
  22 + @Override
  23 + public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  24 + Authentication result = adminAuthenticationProvider.authenticate(authentication);
  25 + if (Objects.nonNull(result)) {
  26 + return result;
  27 + }
  28 + throw new ProviderNotFoundException("Authentication failed!");
  29 + }
  30 +}
  1 +package com.tianbo.warehouse.security.login;
  2 +
  3 +import com.tianbo.warehouse.security.handel.MyAuthenticationFailHandler;
  4 +import com.tianbo.warehouse.security.handel.MyAuthenticationSuccessHandler;
  5 +import com.tianbo.warehouse.util.RedisUtils;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.beans.factory.annotation.Autowired;
  8 +import org.springframework.security.authentication.AuthenticationServiceException;
  9 +import org.springframework.security.authentication.BadCredentialsException;
  10 +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  11 +import org.springframework.security.core.Authentication;
  12 +import org.springframework.security.core.AuthenticationException;
  13 +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
  14 +import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
  15 +import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
  16 +import org.springframework.stereotype.Component;
  17 +
  18 +
  19 +import javax.servlet.http.HttpServletRequest;
  20 +import javax.servlet.http.HttpServletResponse;
  21 +
  22 +
  23 +/**
  24 + * 用户登录自定义校验过滤器
  25 + */
  26 +@Slf4j
  27 +@Component
  28 +public class MyLoginAuthenticationProcessFilter extends AbstractAuthenticationProcessingFilter {
  29 +
  30 + @Autowired
  31 + private RedisUtils redisUtils;
  32 +
  33 + /**
  34 + * @param authenticationManager: 认证管理器
  35 + * @param adminAuthenticationSuccessHandler: 认证成功处理
  36 + * @param adminAuthenticationFailureHandler: 认证失败处理
  37 + */
  38 + public MyLoginAuthenticationProcessFilter(MyAuthenticationManager authenticationManager, MyAuthenticationSuccessHandler adminAuthenticationSuccessHandler, MyAuthenticationFailHandler adminAuthenticationFailureHandler) {
  39 + super(new AntPathRequestMatcher("/login", "POST"));
  40 + this.setAuthenticationManager(authenticationManager);
  41 + this.setAuthenticationSuccessHandler(adminAuthenticationSuccessHandler);
  42 + this.setAuthenticationFailureHandler(adminAuthenticationFailureHandler);
  43 + }
  44 +
  45 + @Override
  46 + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
  47 + if (request.getContentType() == null || !request.getContentType().contains("application/x-www-form-urlencoded")) {
  48 + throw new AuthenticationServiceException("请求头类型不支持: " + request.getContentType());
  49 + }
  50 +
  51 + UsernamePasswordAuthenticationToken authRequest;
  52 + try {
  53 + String loginUserName = request.getParameter("username");
  54 + String loginUserPass = request.getParameter("password");
  55 + String loginVerify = request.getParameter("verify");
  56 + String verifyToken = request.getParameter("verifyToken");
  57 +
  58 + //验证码判断
  59 + String verify = "";
  60 +
  61 + verify = redisUtils.get("verifyToken:" + verifyToken);
  62 +
  63 + if(verify != null && loginVerify != null && verify.equals(loginVerify)){
  64 + authRequest = new UsernamePasswordAuthenticationToken(loginUserName,loginUserPass, null);
  65 + authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
  66 + }else {
  67 + System.out.println("验证码错误!");
  68 + throw new BadCredentialsException("验证码错误!");
  69 + }
  70 + } catch (BadCredentialsException e){
  71 + throw new PreAuthenticatedCredentialsNotFoundException(e.getMessage());
  72 + }catch (Exception e) {
  73 + throw new AuthenticationServiceException(e.getMessage());
  74 + }
  75 + return this.getAuthenticationManager().authenticate(authRequest);
  76 + }
  77 +}
  1 +package com.tianbo.warehouse.security.login;
  2 +
  3 +import com.tianbo.warehouse.dao.USERSMapper;
  4 +import com.tianbo.warehouse.model.USERS;
  5 +import com.tianbo.warehouse.security.CustomUserDetailService;
  6 +import com.tianbo.warehouse.security.config.SM3PasswordEncoder;
  7 +import com.tianbo.warehouse.util.RedisUtils;
  8 +import org.apache.shiro.codec.CodecException;
  9 +import org.apache.shiro.crypto.hash.Hash;
  10 +import org.apache.shiro.crypto.hash.SimpleHash;
  11 +import org.apache.shiro.util.ByteSource;
  12 +import org.springframework.beans.factory.annotation.Autowired;
  13 +import org.springframework.security.authentication.AuthenticationProvider;
  14 +import org.springframework.security.authentication.BadCredentialsException;
  15 +import org.springframework.security.authentication.DisabledException;
  16 +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  17 +import org.springframework.security.core.Authentication;
  18 +import org.springframework.security.core.AuthenticationException;
  19 +import org.springframework.stereotype.Component;
  20 +import javax.annotation.Resource;
  21 +import java.util.Arrays;
  22 +import org.apache.shiro.codec.CodecSupport;
  23 +
  24 +/**
  25 + * 自定义认证处理类
  26 + */
  27 +@Component
  28 +public class MyLoginAuthenticationProvider extends CodecSupport implements AuthenticationProvider {
  29 +
  30 + @Autowired
  31 + private CustomUserDetailService userDetailsService;
  32 +
  33 + @Resource
  34 + private USERSMapper userMapper;
  35 +
  36 + @Autowired
  37 + private RedisUtils redisUtils;
  38 +
  39 + @Autowired
  40 + SM3PasswordEncoder sm3PasswordEncoder;
  41 +
  42 + @Override
  43 + public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  44 + // 获取前端表单中输入后返回的用户名、密码
  45 + String userName = (String) authentication.getPrincipal();
  46 + String password = (String) authentication.getCredentials();
  47 + USERS userInfo = (USERS) userDetailsService.loadUserByUsername(userName);
  48 + //验证登录密码是否符合规则,如位数包含的字符等
  49 + boolean isValid = true;
  50 + // 验证密码
  51 + if (!isValid) {
  52 + //todo: 登录次数超了 锁定账户
  53 + throw new BadCredentialsException("密码错误!");
  54 + }
  55 + if(!userInfo.isEnabled()){
  56 + throw new DisabledException("用户被禁用");
  57 + }
  58 +
  59 + /**
  60 + * ----------------
  61 + */
  62 +
  63 + //取盐规则
  64 + byte[] salt = PasswordSaltUtils.getSalt16(userInfo.getPassword());
  65 + //真实密码
  66 + String realPass = PasswordSaltUtils.getPassword16(userInfo.getPassword());
  67 + //用户登录密码与盐运算
  68 + Object tokenHashedCredentials = this.hashProvidedCredentials(password,salt,1024);
  69 +
  70 + /**
  71 + * ----------------
  72 + */
  73 +
  74 + try{
  75 + String loginUserLock = redisUtils.get("user:lock:"+userName);
  76 + Integer loginUserLockNO= 0;
  77 + if (loginUserLock!=null){
  78 + loginUserLockNO = Integer.valueOf(loginUserLock);
  79 + }
  80 + //判断密码是否正确
  81 + String sm3EncodePassword = sm3PasswordEncoder.encode(password);
  82 + if(!sm3PasswordEncoder.matches(password,userInfo.getPassword())){
  83 + redisUtils.set("user:lock:"+userName, String.valueOf(++loginUserLockNO),120);
  84 +
  85 + //两分钟内错误登录次数超过5次锁定账户
  86 + if (loginUserLockNO>5){
  87 + USERS user = new USERS();
  88 + user.setUsername(userName);
  89 + user.setUserId(userInfo.getUserId());
  90 + //锁定用户
  91 + user.setState(false);
  92 + userMapper.updateByPrimaryKeySelective(user);
  93 + }
  94 + return null;
  95 + }
  96 + }catch (Exception e){
  97 + throw new BadCredentialsException("验证失败!");
  98 + }
  99 +
  100 +
  101 + // 前后端分离情况下 处理逻辑...
  102 + // 更新登录令牌 - 之后访问系统其它接口直接通过token认证用户权限...
  103 + return new UsernamePasswordAuthenticationToken(userInfo, password, userInfo.getAuthorities());
  104 + }
  105 +
  106 + @Override
  107 + public boolean supports(Class<?> aClass) {
  108 + return true;
  109 + }
  110 +
  111 + /**
  112 + * 根据用户密码生成秘文
  113 + * @param credentials 用户登录密码
  114 + * @param salt 盐
  115 + * @param hashIterations 1024
  116 + * @return
  117 + */
  118 + protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
  119 + String hashAlgorithmName = "SHA-1";
  120 + hashIterations = 1024;
  121 + return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
  122 + }
  123 +
  124 + protected boolean equals(Object tokenCredentials, Object accountCredentials) {
  125 +
  126 + if (this.isByteSource(tokenCredentials) && this.isByteSource(accountCredentials)) {
  127 +
  128 + byte[] tokenBytes = this.toBytes(tokenCredentials);
  129 + byte[] accountBytes = this.toBytes(accountCredentials);
  130 + return Arrays.equals(tokenBytes, accountBytes);
  131 + } else {
  132 + return accountCredentials.equals(tokenCredentials);
  133 + }
  134 + }
  135 +
  136 +}
  1 +package com.tianbo.warehouse.security.login;
  2 +
  3 +import org.springframework.stereotype.Component;
  4 +
  5 +@Component
  6 +public class PasswordSaltUtils {
  7 +
  8 + /**
  9 + * 密码校验
  10 + * @return true密码校验通过 false 失败
  11 + */
  12 + public static boolean isValidPassword(String LoginPassword, String UserPassword, String Salt){
  13 + return true;
  14 + }
  15 +
  16 + /**
  17 + * 加密前端传递过来的密码
  18 + * @param Salt 盐
  19 + * @param SaltFromDB 数据库中保存的盐
  20 + * @return
  21 + */
  22 + public static String encodePassword(String Salt,String SaltFromDB){
  23 + return null;
  24 + }
  25 +
  26 + /**
  27 + * 解码存储到数据库中密码密文的前16位
  28 + * @param userPasswordInDB 数据库中用户的密码
  29 + * @return
  30 + */
  31 + public static byte[] getSalt16(String userPasswordInDB){
  32 + byte[] salt = Encodes.decodeHex(userPasswordInDB.substring(0,16));
  33 + return salt;
  34 + }
  35 +
  36 + /**
  37 + * 取存储到数据库中密码密文的后16位
  38 + * @param userPasswordInDB 数据库中用户的密码
  39 + * @return
  40 + */
  41 + public static String getPassword16(String userPasswordInDB){
  42 +
  43 + return userPasswordInDB.substring(16);
  44 + }
  45 +}
  1 +package com.tianbo.warehouse.security.login;
  2 +
  3 +import org.bouncycastle.crypto.digests.SM3Digest;
  4 +import org.bouncycastle.jce.provider.BouncyCastleProvider;
  5 +
  6 +import java.io.UnsupportedEncodingException;
  7 +import java.security.Security;
  8 +
  9 +
  10 +/**
  11 + * sm3生成密码摘要
  12 + * 需要依赖 Bouncy Castle轻量级密码术包 1.60
  13 + */
  14 +public class SM3EncryptUtil {
  15 +
  16 + private static byte[] SECRET_KEY = {101, 87, 99, 10, 34, 45, 77, 76, 98, 13, 12, 18, 73, 84, 91, 93};
  17 +
  18 + public static byte[] hash(byte[] srcData) {
  19 + SM3Digest digest = new SM3Digest();
  20 + digest.update(srcData, 0, srcData.length);
  21 + byte[] hash = new byte[digest.getDigestSize()];
  22 + digest.doFinal(hash, 0);
  23 + return hash;
  24 + }
  25 +
  26 + public static String bytetoString(byte[] digest) {
  27 + String str = "";
  28 + String tempStr = "";
  29 + for (int i = 0; i < digest.length; i++) {
  30 + tempStr = (Integer.toHexString(digest[i] & 0xff));
  31 + if (tempStr.length() == 1) {
  32 + str = str + "0" + tempStr;
  33 + } else {
  34 + str = str + tempStr;
  35 + }
  36 + }
  37 + return str.toLowerCase();
  38 + }
  39 +
  40 + /**
  41 + * 密码SM3加密
  42 + * @param password
  43 + * @return
  44 + */
  45 + public static String passwordSm3(String password){
  46 + Security.addProvider(new BouncyCastleProvider());
  47 + try {
  48 + byte[] pwdBytes = password.getBytes("UTF-8");
  49 + byte[] pwdDigest = hash(pwdBytes);
  50 + return bytetoString(pwdDigest);
  51 + } catch (Exception e) {
  52 + e.printStackTrace();
  53 + return null;
  54 + }
  55 + }
  56 +
  57 + public static void main(String[] args) {
  58 + Security.addProvider(new BouncyCastleProvider());
  59 + try {
  60 + String pwdDigest = passwordSm3("vmvnv1v2VV");
  61 + System.out.println(pwdDigest);
  62 + } catch (Exception e) {
  63 + e.printStackTrace();
  64 + }
  65 + }
  66 +}
  67 +
  1 +package com.tianbo.warehouse.security.login;
  2 +
  3 +import javax.crypto.BadPaddingException;
  4 +import javax.crypto.Cipher;
  5 +import javax.crypto.IllegalBlockSizeException;
  6 +import javax.crypto.NoSuchPaddingException;
  7 +import javax.crypto.spec.SecretKeySpec;
  8 +import java.security.*;
  9 +import java.util.Base64;
  10 +
  11 +/**
  12 + * sm4加密工具
  13 + * 需要依赖 Bouncy Castle轻量级密码术包 1.60
  14 + */
  15 +public class SM4EncryptUtil {
  16 +
  17 + /**
  18 + * 密钥, 禁止修改
  19 + */
  20 + private static byte[] SECRET_KEY = {101, 87, 99, 10, 34, 45, 77, 76, 98, 13, 12, 18, 73, 84, 91, 93};
  21 +
  22 + /**
  23 + * 解密方法
  24 + */
  25 + public static byte[] decryptSM4( byte[] cipherText)
  26 + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
  27 + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
  28 + return decryptSM4(SECRET_KEY, cipherText);
  29 + }
  30 +
  31 + public static byte[] decryptSM4(byte[] key, byte[] cipherText)
  32 + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
  33 + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
  34 + Cipher cipher = generateEcbCipher("SM4/ECB/PKCS5Padding", Cipher.DECRYPT_MODE, key);
  35 + return cipher.doFinal(cipherText);
  36 + }
  37 +
  38 + /**
  39 + * 解密方法
  40 + */
  41 + public static byte[] encryptSM4( byte[] cipherText)
  42 + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
  43 + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
  44 + return encryptSM4(SECRET_KEY, cipherText);
  45 + }
  46 +
  47 + public static byte[] encryptSM4(byte[] key, byte[] data)
  48 + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
  49 + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
  50 + Cipher cipher = generateEcbCipher("SM4/ECB/PKCS5Padding", Cipher.ENCRYPT_MODE, key);
  51 + return cipher.doFinal(data);
  52 + }
  53 +
  54 + private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key)
  55 + throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
  56 + InvalidKeyException {
  57 + Cipher cipher = Cipher.getInstance(algorithmName, "BC");
  58 + Key sm4Key = new SecretKeySpec(key, "SM4");
  59 + cipher.init(mode, sm4Key);
  60 + return cipher;
  61 + }
  62 +
  63 + public String pwd(String pwd){
  64 + try {
  65 + String encryptPwd = new String(Base64.getEncoder().encode(encryptSM4(pwd.getBytes("UTF-8"))));
  66 + return encryptPwd;
  67 + } catch (Exception e) {
  68 + e.printStackTrace();
  69 + return "";
  70 + }
  71 + }
  72 +
  73 +}
@@ -4,11 +4,15 @@ import com.github.pagehelper.PageInfo; @@ -4,11 +4,15 @@ import com.github.pagehelper.PageInfo;
4 import com.tianbo.warehouse.model.ROLE; 4 import com.tianbo.warehouse.model.ROLE;
5 import com.tianbo.warehouse.model.RolePermission; 5 import com.tianbo.warehouse.model.RolePermission;
6 6
  7 +import java.util.List;
  8 +
7 public interface RoleService { 9 public interface RoleService {
8 PageInfo<ROLE> findAll(int pageNum, int pageSize, String roleName, String type); 10 PageInfo<ROLE> findAll(int pageNum, int pageSize, String roleName, String type);
9 11
10 public PageInfo<ROLE> orgSelectWithRootNoTree(ROLE role,int pageNum, int pageSize); 12 public PageInfo<ROLE> orgSelectWithRootNoTree(ROLE role,int pageNum, int pageSize);
11 13
  14 + List<ROLE> getROLE_anonymousPermList();
  15 +
12 int insertSelective(ROLE record); 16 int insertSelective(ROLE record);
13 17
14 int setRolePermissoin(RolePermission record); 18 int setRolePermissoin(RolePermission record);
@@ -109,6 +109,12 @@ public class RoleServiceImp implements RoleService{ @@ -109,6 +109,12 @@ public class RoleServiceImp implements RoleService{
109 return roleMapper.insertSelective(record); 109 return roleMapper.insertSelective(record);
110 } 110 }
111 111
  112 + @Override
  113 + public List<ROLE> getROLE_anonymousPermList() {
  114 + List<ROLE> list = roleMapper.findAll("ROLE_anonymous", null);
  115 + return list;
  116 + }
  117 +
112 @RedisCacheDelTarget(cacheKey = "getUserMenuTreeByUserId") 118 @RedisCacheDelTarget(cacheKey = "getUserMenuTreeByUserId")
113 @Transactional(rollbackFor = Exception.class) 119 @Transactional(rollbackFor = Exception.class)
114 @Override 120 @Override
  1 +package com.tianbo.warehouse;
  2 +
  3 +import com.tianbo.warehouse.security.config.SM3PasswordEncoder;
  4 +import com.tianbo.warehouse.security.login.SM4EncryptUtil;
  5 +import org.junit.Test;
  6 +import org.springframework.boot.test.context.SpringBootTest;
  7 +
  8 +public class SM3EncodeTest {
  9 +
  10 + @Test
  11 + public void encode(){
  12 + String password = "vmvnv1v2VV";
  13 + SM3PasswordEncoder encoder =new SM3PasswordEncoder();
  14 + String encode = encoder.encode(password);
  15 + System.out.println("SM3encode = " + encode);
  16 +
  17 + String SM4encode = new SM4EncryptUtil().pwd(password);
  18 + System.out.println("SM4encode = " + SM4encode);
  19 +
  20 + }
  21 +
  22 +}