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

}

 

posted @ 2024-08-28 14:54  此时不卷何时卷  阅读(34)  评论(0编辑  收藏  举报