springsecurity6学习
一、maven依赖
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <spring.version>3.2.0</spring.version> <commons.version>3.12.0</commons.version> <codec.version>1.15</codec.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons.version}</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>${codec.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.32</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>16</source> <target>16</target> </configuration> </plugin> </plugins> </build>
二、权限加载到内存中
1、config配置
a.CorsConfig.java
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @author Administrator */ @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 是否发送cookie .allowCredentials(true) .allowedOriginPatterns("*") .allowedMethods(new String[]{"GET","POST","PUT","DELETE"}) .allowedHeaders("*") .exposedHeaders("*"); } }
b.SecurityConfig.java
import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.pro.filter.JwtAuthenticationTokenFilter; import org.pro.filter.LoginFilter; import org.pro.model.Resource; import org.pro.service.ResourceService; import org.pro.service.UserService; import org.pro.utils.EncryptionUtils; import org.pro.utils.JwtUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; import org.springframework.util.CollectionUtils; import java.util.List; /** * @author Administrator */ @Configuration @EnableWebSecurity @Slf4j public class SecurityConfig { private ResourceService resourceService; private UserService userService; private AuthenticationConfiguration authenticationConfiguration; /** * 认证链 * @param http * @return * @throws Exception */ @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { List<Resource> resources = this.resourceService.getAllResources(); if (!CollectionUtils.isEmpty(resources) && resources.size()>0) { http.authorizeHttpRequests(auth -> { for (Resource resource : resources) { String authorityCode = resource.getAuthorityCode(); if (StringUtils.isNotBlank(authorityCode)) { auth.requestMatchers(resource.getUrl()).hasAuthority(authorityCode); } else { auth.requestMatchers(resource.getUrl()).permitAll(); } } auth.anyRequest().authenticated(); }); } // 登录 // loginPage:登录页面url // loginProcessingUrl:登录表单提交url http.formLogin(formLogin -> formLogin.loginProcessingUrl("/login")); // 登陆之前获取并校验token http.addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); // 自定义登录过滤器,使用addFilterAt时,权限出错,可能原因是过滤器位置的问题 http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class); // 前后端分离时,使用addFilterAfter会返回一个登录的html页面代码 // http.addFilterAfter(loginFilter(), UsernamePasswordAuthenticationFilter.class); http.csrf(csrf -> csrf.disable()); // cors跨域关闭 // http.cors(cors -> cors.disable()); // 前后端未分离使用 // http.rememberMe(rememberMe -> rememberMe.rememberMeParameter("rememberMe").rememberMeCookieName("rememberMe")); //http.logout(login -> login.logoutUrl("/logout")); //http.exceptionHandling(e -> e.accessDeniedPage("/login/error")); return http.build(); } @Bean public UserDetailsService userDetailsService() { return loginName -> { org.pro.model.User user = this.userService.getUserByName(loginName); if (user == null) { throw new UsernameNotFoundException("用户未找到"); } List<String> codes = user.getCodes(); String[] authCodeArr = new String[codes.size()]; return User.withUsername(loginName).password(user.getPassword()).authorities(codes.toArray(authCodeArr)).build(); }; } @Bean public RememberMeServices rememberMeServices(UserDetailsService userDetailsService) { TokenBasedRememberMeServices.RememberMeTokenAlgorithm encodingAlgorithm = TokenBasedRememberMeServices.RememberMeTokenAlgorithm.SHA256; TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices("myKey", userDetailsService, encodingAlgorithm); rememberMe.setMatchingAlgorithm(TokenBasedRememberMeServices.RememberMeTokenAlgorithm.MD5); return rememberMe; } /** * 启动注入会调用 * @return * @throws Exception */ @Bean public LoginFilter loginFilter() throws Exception { LoginFilter loginFilter = new LoginFilter(); loginFilter.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager()); loginFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler()); loginFilter.setAuthenticationFailureHandler(authenticationFailureHandler()); return loginFilter; } @Bean public AuthenticationSuccessHandler authenticationSuccessHandler() { return (HttpServletRequest request, HttpServletResponse response, Authentication authentication) -> { System.out.println("==========登录校验成功=========="); // response.sendRedirect("/index"); /*response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("==========登陆成功==========");*/ // 结果为"" // System.out.println("getContextPath:"+request.getContextPath()); // 结果为/login // System.out.println("getRequestURI:"+request.getRequestURI()); // 结果为/login // System.out.println("getServletPath:"+request.getServletPath()); // 结果为http://localhost:8081/login // System.out.println("getRequestURL:"+request.getRequestURL()); // response.sendRedirect(request.getRequestURI()); Cookie cookie = new Cookie("rememberMe", JwtUtils.genToken(authentication)); cookie.setAttribute("test", "test"); response.addCookie(cookie); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("登录成功"); }; } @Bean public AuthenticationFailureHandler authenticationFailureHandler() { return (HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) -> { System.out.println("==========登录校验失败=========="); exception.printStackTrace(); }; } @Bean public PasswordEncoder passwordEncoder() { return new PasswordEncoder() { @Override public String encode(CharSequence rawPassword) { if (rawPassword == null) { throw new IllegalArgumentException("rawPassword cannot be null"); } return EncryptionUtils.sha256(rawPassword.toString()); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { if (encodedPassword == null || encodedPassword.length() == 0) { log.info("Empty encoded password"); return false; } return encode(rawPassword).equals(encodedPassword); } }; } @Autowired public void setResourceService(ResourceService resourceService) { this.resourceService = resourceService; } @Autowired public void setUserService(UserService userService) { this.userService = userService; } @Autowired public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) { this.authenticationConfiguration = authenticationConfiguration; } }
2、filter过滤器
a.JwtAuthenticationTokenFilter.java
import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.pro.utils.JwtUtils; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; /** * @author Administrator */ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String name = "rememberMe"; Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (name.equals(cookie.getName())) { System.out.println("已获取到token"); try { JwtUtils.tokenVerify(cookie.getValue()); } catch (Exception e) { response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("非法token"); return; } } } } filterChain.doFilter(request, response); } }
b.LoginFilter.java
import com.alibaba.fastjson.JSON; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.pro.model.User; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import java.io.BufferedReader; import java.io.IOException; import java.util.Map; /** * @author Administrator */ public class LoginFilter extends UsernamePasswordAuthenticationFilter { private final String method = "POST"; @SneakyThrows @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (! method.equals(request.getMethod()) ) { throw new AuthenticationServiceException("请求方法错误,请求方法应为POST,当前请求方法是:" + request.getMethod()); } User user = obtainUser(request); UsernamePasswordAuthenticationToken authentication = UsernamePasswordAuthenticationToken.unauthenticated(user.getLoginName(), user.getPassword()); // 此处可能报错,AuthenticationManager可能为空 return this.getAuthenticationManager().authenticate(authentication); } private User obtainUser(HttpServletRequest request) throws IOException { User user = new User(); String username = request.getParameter("username"); String password = request.getParameter("password"); if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { BufferedReader reader = request.getReader(); StringBuffer sb = new StringBuffer(); String line = null; if ((line = reader.readLine()) != null) { sb.append(line); } reader.close(); Map<String, String> map = JSON.parseObject(sb.toString(), Map.class); username = map.get("username"); password = map.get("password"); if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { throw new AuthenticationServiceException("用户或密码为空"); } } user.setLoginName(username); user.setPassword(password); return user; } }
3、工具类
a.AESUtils.java
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.Key; import java.util.Base64; /** * @author Administrator */ public class AESUtils { static Logger logger = LoggerFactory.getLogger(AESUtils.class); /** * 密钥 */ public static String key = "1A2B3C4D5E6F7G8h"; /** * 密钥算法 */ public static final String KEY_ALGORITHM = "AES"; /** * 加密/解密算法 / 工作模式 / 填充方式 */ public static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding"; private static final Base64.Decoder DECODE_64 = Base64.getDecoder(); private static final Base64.Encoder ENCODE_64 = Base64.getEncoder(); private static final String CHARSET = "utf-8"; /** * 转换密钥 * @param key 二进制密钥 * @return Key 密钥 * @throws Exception */ private static Key toKey(byte[] key) { // 实例化AES密钥材料 SecretKey secretKey = new SecretKeySpec(key, KEY_ALGORITHM); return secretKey; } /** * 解密 * @param content 待解密数据 * @return byte[] 解密数据 * @throws Exception */ public static String decrypt(String content) throws Exception { // 还原密钥 Key k = toKey(key.getBytes(CHARSET)); byte[] data = content.getBytes(CHARSET); // PKCS5Padding实例化,使用PKCS7Padding填充方式是Cipher.getInstance(CIPHER_ALGORITHM, "BC") Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 初始化,设置为解密模式 cipher.init(Cipher.DECRYPT_MODE, k); // 执行操作 return new String(cipher.doFinal(DECODE_64.decode(data))); } /** * 加密 * @param content 待加密数据 * @return byte[] 加密数据 * @throws Exception */ public static String encrypt(String content) throws Exception { // 还原密钥 Key k = toKey(key.getBytes(StandardCharsets.UTF_8)); byte[] data = content.getBytes(CHARSET); // PKCS5Padding实例化,使用PKCS7Padding填充方式是Cipher.getInstance(CIPHER_ALGORITHM, "BC") Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 初始化,设置为加密模式 cipher.init(Cipher.ENCRYPT_MODE, k); // 执行操作 return ENCODE_64.encodeToString(cipher.doFinal(data)); } }
b.CommonUtils.java
import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.cglib.beans.BeanMap; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Pattern; /** * @author Administrator */ public class CommonUtils { private static final Map<String, Object> map = new HashMap<String, Object>(); private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm"; /** * 2019年10月4日16:56:58 隐藏身份证后六位 * * @param identityCard * @return */ public static String hidIdentityCard(String identityCard) { // 增加身份证校验 String pattern = "(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)"; if (StringUtils.isNotBlank(identityCard)) { if (Pattern.compile(pattern).matcher(identityCard).matches()) { return identityCard.substring(0, identityCard.length() - 6) + "xxxxxx"; } else { throw new CustomException("身份证校验失败"); } } else { throw new NullPointerException("身份证不能为空"); } } public static Map<String, String> switchMapToString(HttpServletRequest request) { Map<String, String[]> paramMap = request.getParameterMap(); Map<String, String> switchMap = new HashMap<>(); if (paramMap.isEmpty()) { return switchMap; } for (Map.Entry<String, String[]> entry : paramMap.entrySet()) { String key = entry.getKey(); String value = entry.getValue()[0]; if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) { switchMap.put(entry.getKey(), entry.getValue()[0]); } } return switchMap; } public static Map<String, Object> switchMapToObject(HttpServletRequest request) { Map<String, String[]> paramMap = request.getParameterMap(); Map<String, Object> switchMap = new HashMap<>(); if (paramMap.isEmpty()) { return switchMap; } for (Map.Entry<String, String[]> entry : paramMap.entrySet()) { String key = entry.getKey(); String value = entry.getValue()[0]; if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) { switchMap.put(entry.getKey(), request.getParameter(key)); } } return switchMap; } /** * admin:构建一个干净的map,不需要每次都new一个新的 * * @return */ public static Map<String, Object> getClearMap() { if (!map.isEmpty()) { map.clear(); } return map; } /** * 按分隔符将字符串转成集合,此几个不可增删 * * @param str * @param separator * @return */ public static List<String> splitStrBySeparator(String str, String separator) { if (StringUtils.isBlank(str)) { throw new NullPointerException("切割的字符串不能为空!"); } if (StringUtils.isBlank(separator)) { separator = ","; } return Arrays.asList(str.split(separator)); } public static List<Long> splitStrToLongBySeparator(String str, String separator) { List<Long> longList = new ArrayList<>(); List<String> stringList = splitStrBySeparator(str, separator); stringList.forEach(string -> { longList.add(Long.parseLong(string)); }); return longList; } public static String[] listToArray(List<String> stringList) { String[] stringArray = new String[stringList.size()]; for (int i = 0; i < stringList.size(); i++) { stringArray[i] = stringList.get(i); } return stringArray; } /** * 通过反射将javaBean对象转成Map * * @param obj -->javaBean * @param nullFlag -->空值是否添加到map中 * @return 返回key为属性名,value为属性值的map */ public static Map<String, Object> objToMap(Object obj, boolean nullFlag) { Map<String, Object> map = new HashMap<>(16); try { Class<?> clazz = obj.getClass(); for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); Object value = field.get(obj); if (nullFlag && value == null) { String fieldName = field.getName(); map.put(fieldName, value); } } } catch (Exception e) { e.printStackTrace(); } return map; } public static <T> T mapToBean(T bean, Map<String, Object> map) { BeanMap beanMap = BeanMap.create(bean); beanMap.putAll(map); return bean; } public static <T> T mapToBean(Map<String, Object> map, Class<T> beanType) throws Exception { // 创建javaBean对象 Object obj = beanType.newInstance(); BeanInfo beanInfo = Introspector.getBeanInfo(beanType, Object.class); PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { // 从Map中获取属性同名的key值 Object value = map.get(pd.getName()); // 调用setter方法设置属性值 pd.getWriteMethod().invoke(obj, value); } return (T)obj; } public static String getIPAddress(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); String unknown = "unknown"; if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } public static Date getDate(String date) { if (StringUtils.isBlank(date)) { throw new CustomException("时间转换的时间字符串不能为空"); } try { SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); Date parse = sdf.parse(date); return parse; } catch (Exception e) { e.printStackTrace(); return null; } } }
c.CustomException.java
/** * @author Administrator */ public class CustomException extends RuntimeException { public CustomException() {} public CustomException(String message) { // 把参数传递给Throwable的带String参数的构造方法 super(message); } }
d.EncryptionUtils.java
import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.security.MessageDigest; /** * @author Administrator */ @Slf4j public class EncryptionUtils { private static final String SALT_VALUE = "hash_algorithm"; private static final String ENCRYPTION_SHA256 = "SHA-256"; private static final String ENCRYPTION_MD5 = "MD5"; private static final String DEFAULT_ID_PREFIX = "{"; private static final String DEFAULT_ID_SUFFIX = "}"; /** * SHA-2加密,安全性高于SHA-1,sha256和sha512都属于SHA-2 * @param input * @return */ public static String sha256(String input) { try { if (StringUtils.isBlank(input)) { log.info("**********输入不能为空**********"); throw new NullPointerException("输入不能为空"); } // 添加盐值 input = input + SALT_VALUE; MessageDigest digest = MessageDigest.getInstance("SHA-256"); digest.update(input.getBytes()); byte[] md = digest.digest(); StringBuilder sb = new StringBuilder(); for (byte b : md) { sb.append(String.format("%02x", b)); } return DEFAULT_ID_PREFIX + "SHA-256" + DEFAULT_ID_SUFFIX + sb.toString(); } catch (Exception e) { System.out.println("**********sha256加密报错**********"); e.printStackTrace(); } return null; } public static String md5(String input) { try { if (StringUtils.isBlank(input)) { log.info("**********输入不能为空**********"); throw new NullPointerException("输入不能为空"); } // 添加盐值 input = input + SALT_VALUE; MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(input.getBytes()); byte[] md = digest.digest(); StringBuilder sb = new StringBuilder(); for (byte b : md) { sb.append(String.format("%02x", b)); } return DEFAULT_ID_PREFIX + "MD5" + DEFAULT_ID_SUFFIX + sb.toString(); } catch (Exception e) { System.out.println("**********md5加密报错**********"); e.printStackTrace(); } return null; } /** * 当前只有sha256和md5加密 * @param input * @param type * @return */ public static String encryption(String input, String type) { try { if(StringUtils.isBlank(input)) { log.info("**********输入不能为空**********"); throw new NullPointerException("输入不能为空"); } if (StringUtils.isBlank(type) || !ENCRYPTION_SHA256.equals(type) || !ENCRYPTION_MD5.equals(type)) { type = ENCRYPTION_SHA256; } // 添加盐值 input = input + SALT_VALUE; MessageDigest digest = MessageDigest.getInstance(type); digest.update(input.getBytes()); byte[] md = digest.digest(); StringBuilder sb = new StringBuilder(); for (byte b : md) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (Exception e) { System.out.println("**********"+type+"加密报错**********"); e.printStackTrace(); } return null; } }
e.JwtAuthentication.java
import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; import java.util.Collection; /** * @author Administrator */ public class JwtAuthentication implements Authentication { private Collection<SimpleGrantedAuthority> authorities; private Object details; private boolean authenticated; private Object principal; private Object credentials; @Override public Collection<SimpleGrantedAuthority> getAuthorities() { return authorities; } public void setAuthorities(Collection<SimpleGrantedAuthority> authorities) { this.authorities = authorities; } @Override public Object getDetails() { return details; } public void setDetails(Object details) { this.details = details; } @Override public boolean isAuthenticated() { return authenticated; } @Override public void setAuthenticated(boolean authenticated) { this.authenticated = authenticated; } @Override public Object getPrincipal() { return principal; } public void setPrincipal(Object principal) { this.principal = principal; } @Override public Object getCredentials() { return credentials; } public void setCredentials(Object credentials) { this.credentials = credentials; } @Override public String getName() { return null; } }
f.JwtUtils.java
import com.alibaba.fastjson.JSON; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import java.util.Date; import java.util.Map; /** * @author Administrator */ @Slf4j public class JwtUtils { private static final String secret = "project"; // 30天 private static final Long EXP_TIME = 1000L*60*60*24*30; /** * 根据验证和时间生成token * @param authentication 验证对象 * @param expTime 过期时间 * @return */ public static String genToken(Authentication authentication, Long expTime) { if (expTime == null) { expTime = EXP_TIME; } Date expDate = new Date(System.currentTimeMillis()+expTime); User user = (User)authentication.getPrincipal(); String userJson = JSON.toJSONString(user); String subject = "jwtToken"; String authenticationJson = JSON.toJSONString(authentication); System.out.println("authenticationJson:"+authenticationJson); return JWT.create() .withSubject("token") // 配置过期时间 .withExpiresAt(expDate) .withSubject(subject) // 设置接收方信息,登录用户信息 .withClaim("user", userJson) .withAudience(authenticationJson) // 签证信息 .sign(Algorithm.HMAC256(secret)); } /** * 生成JWT token * @param authentication * @return */ public static String genToken(Authentication authentication) { return genToken(authentication, EXP_TIME); } /** * 根据map差UN关键token * @param map 需要创建token的信息,保存在withClaim中 * @param expTime 过期时间 * @return */ public static String genToken(Map<String, String> map, Long expTime) { if (expTime == null) { expTime = EXP_TIME; } JWTCreator.Builder builder = JWT.create(); // 设置过期时间 builder.withExpiresAt(new Date(System.currentTimeMillis()+expTime)); map.forEach((key, value) -> { if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) { builder.withClaim(key, value); } }); return builder.sign(Algorithm.HMAC256(secret)); } public static String genToken(Map<String, String> map) { return genToken(map, EXP_TIME); } public static void tokenVerify(String token) { try { JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(secret)).build(); jwtVerifier.verify(token); Date expiresAt = JWT.decode(token).getExpiresAt(); // 判断时间是否过期 if (!expiresAt.after(new Date())) { log.info("==========token已过期=========="); } String json = JWT.decode(token).getAudience().get(0); JwtAuthentication authentication = JSON.parseObject(json, JwtAuthentication.class); log.info("===============token解析的对象:"+authentication); SecurityContextHolder.getContext().setAuthentication(authentication); log.info("==========token校验结束=========="); } catch (Exception e) { e.printStackTrace(); log.info("token校验失败"); } } }
三、数据库加载健全比较
1、config配置
a.SecurityConfig.java
package org.pro.config; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.pro.filter.JwtAuthenticationTokenFilter; import org.pro.filter.LoginFilter; import org.pro.service.ResourceService; import org.pro.service.UserService; import org.pro.utils.EncryptionUtils; import org.pro.utils.JwtUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.intercept.AuthorizationFilter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; import java.util.List; /** * @author Administrator */ @Configuration @EnableWebSecurity @Slf4j public class SecurityConfig { private ResourceService resourceService; private UserService userService; private AuthenticationConfiguration authenticationConfiguration; /** * 认证链 * @param http * @return * @throws Exception */ @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { /*List<Resource> resources = this.resourceService.getAllResources(); if (!CollectionUtils.isEmpty(resources) && resources.size()>0) { http.authorizeHttpRequests(auth -> { for (Resource resource : resources) { String authorityCode = resource.getAuthorityCode(); if (StringUtils.isNotBlank(authorityCode)) { auth.requestMatchers(resource.getUrl()).hasAuthority(authorityCode); } else { auth.requestMatchers(resource.getUrl()).permitAll(); } } auth.anyRequest().authenticated(); }); }*/ // 登录 // loginPage:登录页面url // loginProcessingUrl:登录表单提交url http.formLogin(formLogin -> formLogin.loginProcessingUrl("/login")); // 登陆之前获取并校验token http.addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); // 自定义登录过滤器,使用addFilterAt时,权限出错,可能原因是过滤器位置的问题 http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class); // 前后端分离时,使用addFilterAfter会返回一个登录的html页面代码 // http.addFilterAfter(loginFilter(), UsernamePasswordAuthenticationFilter.class); http.csrf(csrf -> csrf.disable()); // cors跨域关闭 // http.cors(cors -> cors.disable()); // 前后端未分离使用 // http.rememberMe(rememberMe -> rememberMe.rememberMeParameter("rememberMe").rememberMeCookieName("rememberMe")); //http.logout(login -> login.logoutUrl("/logout")); //http.exceptionHandling(e -> e.accessDeniedPage("/login/error")); return http.build(); } @Bean public AuthorizationFilter authorizationFilter() { return new AuthorizationFilter(new MyAuthorizationManager()); } @Bean public UserDetailsService userDetailsService() { return loginName -> { org.pro.model.User user = this.userService.getUserByName(loginName); if (user == null) { throw new UsernameNotFoundException("用户未找到"); } List<String> codes = user.getCodes(); String[] authCodeArr = new String[codes.size()]; return User.withUsername(loginName).password(user.getPassword()).authorities(codes.toArray(authCodeArr)).build(); }; } @Bean public RememberMeServices rememberMeServices(UserDetailsService userDetailsService) { TokenBasedRememberMeServices.RememberMeTokenAlgorithm encodingAlgorithm = TokenBasedRememberMeServices.RememberMeTokenAlgorithm.SHA256; TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices("myKey", userDetailsService, encodingAlgorithm); rememberMe.setMatchingAlgorithm(TokenBasedRememberMeServices.RememberMeTokenAlgorithm.MD5); return rememberMe; } /** * 启动注入会调用 * @return * @throws Exception */ @Bean public LoginFilter loginFilter() throws Exception { LoginFilter loginFilter = new LoginFilter(); loginFilter.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager()); loginFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler()); loginFilter.setAuthenticationFailureHandler(authenticationFailureHandler()); return loginFilter; } @Bean public AuthenticationSuccessHandler authenticationSuccessHandler() { return (HttpServletRequest request, HttpServletResponse response, Authentication authentication) -> { Cookie cookie = new Cookie("rememberMe", JwtUtils.genToken(authentication)); response.addCookie(cookie); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("登录成功"); }; } @Bean public AuthenticationFailureHandler authenticationFailureHandler() { return (HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) -> { exception.printStackTrace(); Cookie cookie = new Cookie("rememberMe", ""); cookie.setMaxAge(0); response.addCookie(cookie); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("登录失败"); }; } @Bean public PasswordEncoder passwordEncoder() { return new PasswordEncoder() { @Override public String encode(CharSequence rawPassword) { if (rawPassword == null) { throw new IllegalArgumentException("rawPassword cannot be null"); } return EncryptionUtils.sha256(rawPassword.toString()); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { if (encodedPassword == null || encodedPassword.length() == 0) { log.info("Empty encoded password"); return false; } return encode(rawPassword).equals(encodedPassword); } }; } @Autowired public void setResourceService(ResourceService resourceService) { this.resourceService = resourceService; } @Autowired public void setUserService(UserService userService) { this.userService = userService; } @Autowired public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) { this.authenticationConfiguration = authenticationConfiguration; } }
b.新增MyAuthorizationManager.java
package org.pro.config; import jakarta.servlet.http.HttpServletRequest; import lombok.SneakyThrows; import org.pro.service.ConstantService; import org.pro.service.ResourceService; import org.pro.utils.SpringBeanUtils; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.util.CollectionUtils; import java.nio.file.AccessDeniedException; import java.util.Collection; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; /** * @author Administrator */ public class MyAuthorizationManager implements AuthorizationManager<HttpServletRequest> { @SneakyThrows @Override public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) { // 获得请求资源定位 String requestURI = request.getRequestURI(); // 获取匿名code ConstantService constantService = SpringBeanUtils.getBean(ConstantService.class); String code = constantService.getCodeByCategory("no_login"); Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities(); // UserDetailsService创建的user的codes List<String> list = authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); ResourceService resourceService = SpringBeanUtils.getBean(ResourceService.class); // 匹配resource资源中的authorityCode List<String> codes = resourceService.getCodesByURI(requestURI); // 判断是否是匿名路径 if (codes.contains(code)) { return new AuthorizationDecision(true); } if (CollectionUtils.isEmpty(codes) || codes.size()<1 || !contains(codes, list)) { throw new AccessDeniedException("权限不足"); } return new AuthorizationDecision(false); } private boolean contains(List<String> codes, List<String> list) { for (String str : list) { for (String code : codes) { if(str.equals(code)) { return true; } } } return false; } }
2、工具类
a.新增SpringBeanUtils.java
package org.pro.utils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @author Administrator */ @Component public class SpringBeanUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if(SpringBeanUtils.applicationContext == null) { SpringBeanUtils.applicationContext = applicationContext; } } /** * 获取applicationContext * * @return */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 通过name获取bean * * @param name * @return */ public static Object getBean(String name){ return getApplicationContext().getBean(name); } /** * 通过class获取bean * * @param clazz * @param <T> * @return */ public static <T> T getBean(Class<T> clazz) { return getApplicationContext().getBean(clazz); } /** * 通过name,以及clazz返回指定的bean * * @param name * @param clazz * @param <T> * @return */ public static <T> T getBean(String name,Class<T> clazz) { return getApplicationContext().getBean(name, clazz); } }