SpringSecurity登录认证流程及源码分析

news2024/11/15 11:13:28

目录

一 作用

二 流程及源码分析 


一 作用

spring security作为spring家族中的一员,它的主要作用有两个,分别是认证和授权。

我们以前在实现登录功能的时候,前端会传来用户名和密码,然后我们根据前端传来的数据从用户表中的数据进行比较,从而实现用户登录。

而springSecurity的功能也有登录认证,并且它在登录认证成功后,会生成一个认证对象,当登录成功后再发送其他请求,就会根据这个认证对象来判断当前用户是否已经登录。

二 流程及源码分析 

在使用springSecurity之前,你还需要导入它的依赖坐标,只要导入了依赖,它就会自动生效。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

① 用户输入用户名和密码以及验证码访问登录接口:

登录其实也是一个请求,所以也会被springsecurity拦截,但是因为你没有登录,就没有他的认证对象,它就不会让你访问这个接口,所以开始之前还需要在spring security的配置类中放行登录请求。(配置类的代码最后会统一给)

放行登录亲请求:

② 调用逻辑业务service层,完成验证。

package com.fs.system.service.ipml;

import cn.hutool.core.convert.Convert;
import com.fs.common.constant.CacheConstants;
import com.fs.common.constant.UserConstants;
import com.fs.common.core.pojo.SysUser;
import com.fs.common.core.vo.LoginUser;
import com.fs.common.enums.UserStatus;
import com.fs.common.exception.ServiceException;
import com.fs.common.exception.user.BlackListException;
import com.fs.common.exception.user.CaptchaException;
import com.fs.common.exception.user.UserNotExistsException;
import com.fs.common.exception.user.UserPasswordNotMatchException;
import com.fs.common.util.DateUtils;
import com.fs.common.util.RedisCache;
import com.fs.common.util.ip.IpUtils;
import com.fs.system.security.UserDetailsServiceImpl;
import com.fs.system.service.ISysConfigService;
import com.fs.system.service.ISysLoginService;
import com.fs.system.service.ISysUserService;
import com.mysql.cj.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
public class ISysLoginServiceImpl implements ISysLoginService {
    @Autowired
    private ISysUserService sysUserService ;  //进行用户信息校验,包括查询该用户是否存在

    @Autowired
    private SysPasswordService passwordService ;  //密码校验,包括密码输入的正确性已经输入密码的次数

    @Autowired
    private ISysConfigService sysConfigService ; //判断验证码功能有没有开启

    @Autowired
    private RedisCache redisCache ; //用于操作缓存中的数据

    @Autowired
    private TokenService tokenService ; //对token的操作

    @Autowired
    private UserDetailsServiceImpl userDetailsService ; //用来获取LoginUser对象

    @Autowired
    private AuthenticationManager authenticationManager ; //用于获取认证
    /** 登录验证 */
    @Override
    public String login(String username, String password, String code, String uuid) {
//        1.验证码验证码
        validateCaptcha(username , code , uuid);
        System.out.println("验证码校验完成...");
//        2.参数校验
        loginCheck(username , password) ;
        3.根据用户名查询用户
//        SysUser sysUser = sysUserService.selectUserByUserName(username);

//        检验密码
//        passwordService.validate(sysUser , password);  //通过Security帮我们进行处理,不需要自己校验了


//        创建token
//        //先创建一个LoginUser对象,因为返回的的是LoginUser对象
//        LoginUser loginUser = new LoginUser();
//        loginUser.setUserId(sysUser.getUserId());
//        loginUser.setUser(sysUser);

        //        返回一个认证对象
        Authentication authentication = null;
        try{
            //创建一个Authentication认证对象,  属性: authenticated =false, 没有被认证
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            //返回一个认证对象:  被认证认证对象
            authentication = authenticationManager.authenticate(authRequest); //这个认证对象就是一个登录的认证器

        }catch (Exception e){
            throw new  ServiceException("用户不存在或者是密码错误");
        }

        //调用tokenService创建token
        //里面已经给LoginUser赋值token了,不过这里面的token是一个uuid,redis存的是loginUser,LoginUser里面的token又是一个uuid?
        String token = tokenService.createToken((LoginUser) userDetailsService.loadUserByUsername(username));
//        修改登陆时间和登录ip
        LoginUser loginUser= (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
//        返回token给前端(token里面存放着登录用户的信息,)
        System.out.println("token:=========>"+token);
        return token;
    }

    /** 验证码验证码 */
    @Override
    public void validateCaptcha(String username, String code, String uuid) {
        /**
         * code是前端传来的验证码答案,uuid是验证码缓存的key
         */
//        先判断有没有开启验证码功能
        boolean captchaEnabled = sysConfigService.selectCaptchaEnabled();
        if (!captchaEnabled) throw new ServiceException("验证码功能没有开启");
//        然后根据key去查询缓存中的value,判断value和输入的答案是否正确
        String value = redisCache.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + uuid);
//        再将从缓存中拿到的value和code比对
        if (!value.equals(code)){
            throw new CaptchaException();
        }
    }


    /**  记录登录信息 :  */
    @Override
    public void recordLoginInfo(Long userId) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr());
        sysUser.setLoginDate(DateUtils.getNowDate());
        sysUserService.updateUserProfile(sysUser);
    }

    /**  登录前置校验(对请求参数的校验)  */
    private void loginCheck(String username , String password){
//        非空校验
        if (Objects.isNull(username)||Objects.isNull(password)){
            throw new UserNotExistsException() ;
        }
//        长度校验
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH || password.length() > UserConstants.PASSWORD_MAX_LENGTH){
            throw new UserPasswordNotMatchException();
        }

        //用户名如果不在指定范围内
        if (username.length()<UserConstants.USERNAME_MIN_LENGTH||username.length()>UserConstants.USERNAME_MAX_LENGTH){
            throw new UserPasswordNotMatchException() ;
        }

        // IP黑名单校验
        //todo  这个地方调用selectConfigByKey这个方法,但是这个数据库里面根本没有记录黑名单的列
        String blackStr = sysConfigService.selectConfigByKey("sys.login.blackIPList");
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
            throw new BlackListException();
        }
    }
}

 

③ 获取认证对象

我们需要调用spring security中的方法来获取一个认证对象,它会经过一系列的认证,只有最后用户的信息认证成功后才会生成一个认证对象,这说明用户认证成功,否则则说明这个用户认证失败。

 

④ 源码分析

那么我们就从这里作为入口,去看看springsecurity的源码是如何实现用户的账号信息以及密码校验的。

1.进入anthenticate方法后,我们发现这个是一个接口的抽象方法:

2.既然是接口,那我们就找到它的默认实现类ProviderManager,并且找到这个实现的方法:

3.那么我们继续看这个方法,这个方法的核心就是调用登录认证器的authenticate()方法:

 
 

4.我们现在知道这里主要就是调用了登录认证器的authenticate方法,那么我们就进这个方法看看里面实现了什么。最后发现这个方法也是一个接口的抽象方法:

5.然后我们找到它的实现类AbstractUserDetailsAuthenticationProvider,并且找到对应的方法:

6.我们找到实现方法authenticate后,发现它主要进行两步操作,先从缓存中获取到这个用户,如果是null,那么就调用etrieveUser()方法,而第一次登录里面肯什么都没有,所以是null,那么主要就是调用etrieveUser()方法:

7.那么不用多说,直接进入这个方法。但是发现它是一个抽象方法,那么我们肯定就是找到它的实现方法,去看里面的实现逻辑:

8.找到它的实现方法,这就已经到尾了,不过我们还记得一开时我们需要返回的值就是一个用户信息对象,所以这里也是找到了我们心心念念的 loadUserByUsername(username)方法:

9.那么我们肯定就进入这个方法去看看,发现它是接口 UserDetailsService的方法。

10.既然如此,我们只需要实现这个接口重写loadUserByUsername方法,然后从数据路根据用户名查询到用户信息,然后封装成一个loginUser对象,并且对这个用户的基本信息进行一个校验,没问题后就可以返回了,到这里根据用户名查找用户这个验证已经完成了。(不过需要注意,因为这个方法返回的是一个UserDetails,所以loginUser对象需要实现或者继承它才能作为该对象返回loginUser对象)

package com.fs.system.security;

import cn.hutool.core.util.ObjectUtil;
import com.fs.common.core.pojo.SysUser;
import com.fs.common.core.vo.LoginUser;
import com.fs.common.enums.UserStatus;
import com.fs.common.exception.ServiceException;
import com.fs.common.exception.user.UserNotExistsException;
import com.fs.system.mapper.SysUserMapper;
import com.fs.system.service.ipml.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Objects;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private SysUserMapper userMapper ;
    @Autowired
    private TokenService tokenService ;
    @Override
    public UserDetails  loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("username:"+username);
//        从数据库查询用户信息
        SysUser sysUser = userMapper.selectUserByUserName(username);
        System.out.println("查询到用户信息:"+sysUser);

        //验证用户是否存在
        if (Objects.isNull(sysUser)){
            System.out.println("用户不存在");
            throw new UserNotExistsException() ;
        }
//        是否禁用
        if (sysUser.getStatus().equals(UserStatus.DISABLE)){
            throw  new ServiceException("用户已封禁") ;
        }
//        是否删除
        if (sysUser.getDelFlag()== UserStatus.DELETED.getCode()){
            throw new ServiceException("账号已经被删除");
        }

//        创建LoginUser对象
        LoginUser loginUser = creatLoginUser(sysUser);
//        创建token
        tokenService.createToken(loginUser);
        return loginUser ;
    }

    public LoginUser creatLoginUser(SysUser sysUser){
        //先创建一个LoginUser对象,因为返回的的是LoginUser对象
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(sysUser.getUserId());
        loginUser.setUser(sysUser);
        System.out.println("成功查询user对象并且返回:"+loginUser);
        return loginUser ;
    }
}

上面只是完成了用户名查到用户的功能,既然已经查询到用户的信息了,说明也就知道了用户的真实密码,那么我接下来就需要把用户输入的密码和真实密码做对比,所以我们继续回到DaoAuthenticationProvider类中。

在这里的 createSuccessAuthentication()方法中,我们成功看见了密码比对的方法:

我们直接找到源头,发现这个加密方法是一个PasswordEncoder接口的抽象方法,里面主要两个方法,分别用于对前端输入的密码加密,还有把加密后的密码和用户信息中的密码进行比对。

所以,我们要实现自定义的加密以及对密码进行比对,我们只需要实现这个接口,同时完成这个两个方法的实现就可以了:

package com.fs.system.security;

import cn.hutool.core.convert.Convert;
import com.fs.common.util.sign.PasswordUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * @author suke
 * @version 1.0
 * @title MyPasswordEncoder
 * @description  自定义的密码编码
 * @create 2024/7/25 10:58
 */
@Component
public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence rawPassword) {

        return PasswordUtils.generate(Convert.toStr(rawPassword));
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return PasswordUtils.verify(Convert.toStr(rawPassword),encodedPassword);
    }
}
  
PasswordUtils是我们用于密码的加密和比对的一个工具类,我们使用的是md5加密,工具类代码如下:
package com.fs.common.util.sign;

import java.security.MessageDigest;
import java.util.Random;

/**
 * MD5加盐加密
 */    
public class PasswordUtils {
    /**
     * 生成含有 随机盐的密码
     */
    public static String generate(String password) {
        Random r = new Random();
        StringBuilder sb = new StringBuilder(16);
        sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
        int len = sb.length();
        if (len < 16) {  //不够16位,前面补0
            for (int i = 0; i < 16 - len; i++) {
                sb.append("0");
            }
        }

        String salt = sb.toString();
        password = md5Hex(password + salt); //32位的16进制
        char[] cs = new char[48];
        for (int i = 0; i < 48; i += 3) {
            cs[i] = password.charAt(i / 3 * 2);
            char c = salt.charAt(i / 3);
            cs[i + 1] = c;
            cs[i + 2] = password.charAt(i / 3 * 2 + 1);
        }
        return new String(cs);
    }
    /**
     * 校验密码是否正确
     */
    public static boolean verify(String password, String md5) {
        char[] cs1 = new char[32];
        char[] cs2 = new char[16];
        for (int i = 0; i < 48; i += 3) {
            cs1[i / 3 * 2] = md5.charAt(i);
            cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
            cs2[i / 3] = md5.charAt(i + 1);
        }
        String salt = new String(cs2);
        return md5Hex(password + salt).equals(new String(cs1));
    }
    /**
     * 获取十六进制字符串形式的MD5摘要
     */
    public static String md5Hex(String src) {
        try {
            return Md5Utils.hash(src);
        } catch (Exception e) {
            return null;
        }
    }

    public static void main(String[] args) {
        System.out.println(generate("123456"));
        //System.out.println(verify("123456", "02dd65660724d38816641b3a98409fa2080401b489c9ff42"));
    }
}

最后在这里,这个校验过程就完成了,最后会成功创建一个认证对象。

随后,我们只需要创建一个token,返回给前端就欧克了。

这里附上token创建的代码:

package com.fs.system.service.ipml;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fs.common.constant.CacheConstants;
import com.fs.common.constant.Constants;
import com.fs.common.core.vo.LoginUser;
import com.fs.common.util.RedisCache;
import com.fs.common.util.ServletUtils;
import com.fs.common.util.ip.AddressUtils;
import com.fs.common.util.ip.IpUtils;
import com.fs.system.config.TokenProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * token验证处理
 */
@Service
public class TokenService {
    @Autowired
    private TokenProperties tokenProperties;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

    @Autowired
    private RedisCache redisCache;

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(HttpServletRequest request){
        // 获取请求携带的令牌
        String token = getToken(request);
        if (StringUtils.isNotEmpty(token))
        {
            try
            {
                Claims claims = parseToken(token);
                // 解析对应的权限以及用户信息
                String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                String userKey = getTokenKey(uuid);
                LoginUser user = redisCache.getCacheObject(userKey);
                return user;
            }
            catch (Exception e)
            {
            }
        }
        return null;
    }

    /**
     * 设置用户身份信息
     */
    public void setLoginUser(LoginUser loginUser){
        if (Objects.nonNull(loginUser) && StrUtil.isNotEmpty(loginUser.getToken()))
        {
            refreshToken(loginUser);
        }
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String token){
        if (StringUtils.isNotEmpty(token)){
            String userKey = getTokenKey(token);
            redisCache.deleteObject(userKey);
        }
    }

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser){
        String token = UUID.randomUUID(false).toString(true);
        loginUser.setToken(token);
        setUserAgent(loginUser);
        refreshToken(loginUser);

        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        return createToken(claims);
    }

    /**
     * 验证令牌有效期,相差不足20分钟,自动刷新缓存
     *
     * @param loginUser
     * @return 令牌
     */
    public void verifyToken(LoginUser loginUser){
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            refreshToken(loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser){
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + tokenProperties.getExpireTime() * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(loginUser.getToken());
        redisCache.setCacheObject(userKey, loginUser, tokenProperties.getExpireTime(), TimeUnit.MINUTES);
    }

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    public void setUserAgent(LoginUser loginUser){
        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr();
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOs().getName());
    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String createToken(Map<String, Object> claims){
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, tokenProperties.getSecret()).compact();
        return token;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims parseToken(String token){
        return Jwts.parser()
                .setSigningKey(tokenProperties.getSecret())
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token){
        Claims claims = parseToken(token);
        return claims.getSubject();
    }

    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    private String getToken(HttpServletRequest request){
        String token = request.getHeader(tokenProperties.getHeader());
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)){
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    private String getTokenKey(String uuid){
        return CacheConstants.LOGIN_TOKEN_KEY + uuid;
    }
}

 最后再附上spring security配置类的完成代码:

package com.fs.system.config;

import com.fs.common.util.RedisCache;
import com.fs.system.security.JwtAuthenticationTokenFilter;
import com.fs.system.security.MyDaoAuthenticationProvider;
import com.fs.system.security.MyPasswordEncoder;
import com.fs.system.security.TokenAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;

/**
 * @author suke
 * @version 1.0
 * @title SpringSecurityConfiguration
 * @description  springSecurity的自定义配置类
 * @create 2024/7/25 10:14
 */
@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
//    @Qualifier("userDetailsServiceImpl")
    private UserDetailsService userDetailsService;

    @Autowired
    private RedisCache redisCache ;

    @Autowired
    private SysPasswordProperties  passwordProperties ;

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter ;

    @Autowired
    private CorsFilter corsFilter;

    @Autowired
    private TokenAuthenticationEntryPoint tokenAuthenticationEntryPoint;

//    @Autowired
//    private LogoutSuccessHandlerImpl logoutSuccessHandler;
    //对密码进行加密    密码校验
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new MyPasswordEncoder() ;
    }
    //修改用户名,密码
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("进入配置类...");

        //使用MyDaoAuthenticationProvider
        auth.authenticationProvider(new MyDaoAuthenticationProvider(userDetailsService,redisCache,passwordEncoder(),passwordProperties));


    }
修改用户名,密码
//@Override
//protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//    //使用MyDaoAuthenticationProvider
//    auth.authenticationProvider(new MyDaoAuthenticationProvider());
//}

    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 禁用HTTP响应标头
                .headers().cacheControl().disable().and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                .antMatchers("/login","/getRouters", "/captchaImage").permitAll()
                // 静态资源,可匿名访问
                .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();


        //添加过滤器
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        //添加跨域过滤器
        http.addFilterBefore(corsFilter,JwtAuthenticationTokenFilter.class);

        //配置认证失败的入口
        http.exceptionHandling().authenticationEntryPoint(tokenAuthenticationEntryPoint);
//
//        //配置注销
//        http.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
    }


}
  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1963953.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI大模型】分布式训练:深入探索与实践优化

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 一、分布式训练的核心原理二、技术细节与实现框架1. 数据并行与模型并行2. 主流框架 三、面临的挑战与优化策略1. 通信开销2. 数据一致性3. 负载均衡 4.使用示例示例一&#xff1a;TensorFlow中的数据…

深入解析Kubernetes(K8s)的核心技术与应用

一、引言 在云计算和容器化技术迅猛发展的今天&#xff0c;Kubernetes&#xff08;简称K8s&#xff09;凭借其强大的容器编排和管理能力&#xff0c;成为了云原生时代不可或缺的基础设施。本文旨在深入探讨Kubernetes的核心技术、应用场景、优势与挑战&#xff0c;以及如何在实…

图创价值 Live——解锁能源新未来!能源行业图技术解决方案深度探索

随着全球能源结构的深刻变革&#xff0c;能源系统正面临着前所未有的挑战与机遇。新能源的迅猛发展、电力市场化的推进以及电网调度的复杂性不断升级&#xff0c;都对能源系统的智能化、高效化提出了更高要求。为此&#xff0c;我们特别邀请了到了悦数解决方案专家-鲍翰林&…

数据结构第1天作业 7月31日

2.3按位置操作 1&#xff09;按照位置插入数据 void Insert_seqlist_single(Seqlist* sq,int arr_sub,int num){if(sq->posN ){ //判断顺序列表是否为满printf("error");return;}else if(arr_sub<0||arr_sub>sq->pos){printf("error…

微信小程序【五】好玩的点击展开弹框功能

弹出效果 步骤一、index.js步骤二、index.json步骤三、index.wxml步骤四、index.wxss 效果简述&#xff1a;恶搞的好玩点击效果&#xff0c;点击后展开 步骤一、index.js Page({data: {isPlaying: true,animationClass: music-icon,show_menu: false, // 菜单是否激活show_p…

异构算力的调度策略解析与实现

随着云计算、大数据和人工智能技术的飞速发展&#xff0c;异构算力调度成为了一个日益重要的课题。异构算力调度是指针对不同类型的计算资源&#xff08;如CPU、GPU、FPGA等&#xff09;进行合理分配与调度&#xff0c;以提高计算资源的利用率、降低功耗并加速任务执行。本文将…

浮点数的二进制表示

浮点数的二进制表示 浮点数在C/C中对应 float 和 double 类型&#xff0c;我们有必要知道浮点数在计算机中实际存储方式。 IEEE754规定&#xff1a; 单精度浮点数字长32位&#xff0c;尾数长度23&#xff0c;指数长度8,指数偏移量127&#xff1b;双精度浮点数字长64位&#xf…

Yarn UI 时间问题,相差8小时

位置 $HADOOP_HOME/share/hadoop/yarn/hadoop-yarn-common-2.6.1.jar 查看 jar tf hadoop-yarn-common-2.6.1.jar |grep yarn.dt.plugins.js webapps/static/yarn.dt.plugins.js 解压 jar -xvf hadoop-yarn-common-2.6.1.jar webapps/static/yarn.dt.plugins.js inflated: we…

mybatis-plus中出现Field ‘id‘ doesn‘t have a default value问题解决方法

问题分析&#xff1a; 出现这个原因&#xff0c;主要是因为mybatis-plus自身查询的特性&#xff0c;因为查询都是它自己内部设定好的参数&#xff0c;一般为了简便&#xff0c;都会默认自己底层的数据库对应的主键id字段是自增的&#xff0c;也就是mybatis-plus认为不需要id,每…

【Git】.gitignore全局配置与忽略匹配规则详解

设置全局配置 1&#xff09;在C:/Users/用户名/目录下创建.gitignore文件&#xff0c;在里面添加忽略规则。 如何创建 .gitignore 文件&#xff1f; 新建一个.txt文件&#xff0c;重命名&#xff08;包括后缀.txt&#xff09;为 .gitignore 即可。 2&#xff09;将.gitignore设…

Eagle平替?免费超强的素材管理神器!支持多级标签,满足素材快速收集!

作为设计师&#xff0c;你是不是下载了很多类型的素材资源&#xff0c;然而要每次使用的时候&#xff0c;还要通过文件夹一级一级去翻找&#xff0c;非常麻烦&#xff01;还好我找到了一款好用的素材管家神器—千鹿设计助手&#xff0c;如果你之前有用过Eagle或者BillFish的话&…

华为od机试真题:求字符串所有整数最小和(Python)

2024华为OD机试&#xff08;C卷D卷&#xff09;最新题库【超值优惠】Java/Python/C合集 题目描述 1.输入字符串s输出s中包含所有整数的最小和&#xff0c;说明&#xff1a;1字符串s只包含a~z,A~Z,,-&#xff0c; 2.合法的整数包括正整数&#xff0c;一个或者多个0-9组成&…

归并排序 python C C++ 图解 代码 及解析

一&#xff0c;概念及其介绍 归并排序&#xff08;Merge sort&#xff09;是建立在归并操作上的一种有效、稳定的排序算法&#xff0c;该算法是采用分治法(Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff…

大厂linux面试题攻略三之Shell编程

一、Shell编程文本截取类 1.有一个b.txt文本(内容如下)&#xff0c;要求将所有域名截取出来&#xff0c;并统计重复域名出现的次数 http://www.baidu.com/index.html https://www.atguigu. com/index.html http://www.sina.com.cn/1024.html …

二百四十八、Linux——删除/etc/.sudoers文件进程或修改/etc/.sudoers文件内容

一、目的 安装国产化数据库OceanBase的时候&#xff0c;需要创建用户&#xff0c;并在/etc/.sudoers文件中赋予用户root权限 二、删除/etc/.sudoers文件进程 1 报错 W10: Warning: Changing a readonly file E325: ATTENTION Found a swap file by the name "/etc/.su…

二叉树的性质证明

文章目录 二叉树的概念二叉树的性质1. 若规定根结点的层数为1&#xff0c;则一棵非空二叉树的第i层上最多有 2 i − 1 2^{i-1} 2i−1 个结点.2. 若规定根结点的层数为1&#xff0c;则深度为h的二叉树的最大结点数是 2 h − 1 2^h-1 2h−1.3. 对任何一棵二叉树, 如果度为0其叶结…

C++:函数模板与类模板详解

1.函数模板 在构造函数的时候&#xff0c;我们常常会考虑传入的参数的数据类型&#xff0c;比如我们写一个大小比较的函数mycmp(class1 a,class1 b)&#xff0c;则可以写出class1为int,float,double,string等各个种类的mycmp函数&#xff0c;这样会很麻烦&#xff0c;且当我们…

hot100-7-链表1

160相交链表 206反转链表 234回文链表 可以反转后半部分链表或者反转全部链表&#xff0c;然后对比输出 141环形链表 142环形链表2

大模型RAG入门及实践

前言 在大语言模型&#xff08;LLM&#xff09;飞速发展的今天&#xff0c;LLMs 正不断地充实和改进我们周边的各种工具和应用。如果说现在基于 LLM 最火热的应用技术是什么&#xff0c;检索增强生成&#xff08;RAG&#xff0c;Retrieval Augmented Generation&#xff09;技…