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); } }

浙公网安备 33010602011771号