SpringSecurity学习总结

news2024/9/28 0:03:16

目录

一、简介

二、使用

引入

登录验证流程

完整流程

三、案例(登录验证)

三、设置密码加密和解密方式

三、自定义登录

四、定义JWT认证过滤器

简介

流程

JWT(当前未使用)

定义token过滤

配置过滤器为最前

再次总结流程

五、退出登录

六、一些中间配置

七、网关的配置

八、授权

权限控制(开启)

赋予用户权限

从数据库查询权限信息

权限判断注解

自定义权限校验

九、自定义失败处理

十、处理跨域请求

对Springboot配置跨域

Spring Security配置允许跨域

十一、 CSRF

十二、总结


一、简介

安全框架,做web的认证和授权

认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户

授权:经过认证后判断当前用户是否有权限

二、使用

引入

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

登录验证流程

前端登录(携带用户名密密码访问登录接口)------服务端(去和数据库中的用户名和密码进行比较检验,如果正确就生成一个jwt)----jwt传输给前端

登录后访问其他请求,需要携带请求头中携带token----服务器根据token获取相关数据---响应信息

完整流程

原理是一个过滤器链

请求----> UsernamePassword AuthenticationFilter-->ExceptionTranslationFilter-->FilterSecurityLnterceptor-->api
  响应  <------------------------------<--------------------------------<---------------------------------
  等等过滤器
  UsernamePassword AuthenticationFilter :登录页面填写了用户名
  ExceptionTranslationFilter:处理抛出的异常
  FilterSecurityLnterceptor:负责权限校验的过滤器

三、案例(登录验证)

登录
    1、自定义登录接口 
        调用ProviderManager 的方法进行认证,如果通过生成jwt
        把用户信息存入redis中
    2、自定义UserdetilsService这个实现列中去查询数据库
        在这个实现列中去查询数据库
校验:
     1、定义jwt认证过滤器
        获取token
        解析token获取其中的userid
        从redis中获取用户信息
        存入SecurityContexHolder

依赖文件

  <!--redis依赖-->        
 <dependency>            
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 
     <!--    fastjson依赖-->        
 <dependency>            
      <groupId>com.alibaba.fastjson2</groupId>               
     <artifactId>fastjson2</artifactId>
      <version>2.0.26</version> 
</dependency>
   
  <!--        jwt依赖-->    
 <dependency>         
      <groupId>io.jsonwebtoken</groupId> 
      <artifactId>jjwt</artifactId> 
      <version>0.9.0</version>  
 </dependency>

login登录验证

UserDetailsServiceImpl 实现校验,校验的主题

//自定义的登录验证链
//实现UserDetailsService方法即可,返回一个用户对象
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired    
    private MyUserMapper userMapper;    
    @Override    
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
         // 查询用户信息        
         LambdaQueryWrapper<MyUser> queryWrapper=new LambdaQueryWrapper<>();       
          // 等值匹配        
          queryWrapper.eq(MyUser::getName,username);        
          MyUser user2=userMapper.selectOne(queryWrapper);        
          //如果没有查询结果 抛出异常        
          if(Objects.isNull(user2)){
            throw  new RuntimeException("用户不存在或者密码错误");        
            }


        //TODO 查询对应的权限信息        
        // 把数据封装成LoginUser(UserDetails)        
        return new LoginUser(user2);   
        
         }
}

数据库中需要 {noop}1234 来表明是明文

UserDetails

把user封装成校验信息的User--即校验的数据(LoginUser)

//用户数据对象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {

   private MyUser user2;    
   @Override    
   public Collection<? extends GrantedAuthority> getAuthorities() {
      // 返回权限信息        
      return null;    
      }

    @Override    
    public String getPassword() {
        return  user2.getPwd().toString(); //获取用户密码    
        }

    @Override    
    public String getUsername() {
        return user2.getName();    
        }

    @Override    
    public boolean isAccountNonExpired() {
        return true; // 是否每过期    
        }

    @Override    
    public boolean isAccountNonLocked() {
        return true;    
        }

    @Override    
    public boolean isCredentialsNonExpired() {
        return true; //是否没有超时    
        }

    @Override    
    public boolean isEnabled() {
        return true; // 用户是否可用    
        }
}

三、设置密码加密和解密方式

数据库中密码是加密的,网关它会自动解密对比

配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //创建BCryptPasswordEncoder注入容器    
    @Bean    
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();   // 加密以及密码验证方式
        
       }
}

测试使用加密解密

@Autowired
PasswordEncoder passwordEncoder;

@Testpublic void test01(){
    BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();    
    //加密    
    String encode1 = passwordEncoder.encode("1234");//加密    
    log.info("加密一次:{}",encode1);    
    // 密码对比 明文--密文    
    boolean matches = passwordEncoder.matches("1234", "$2a$10$asn3beY//msSOAJjnlmY/ei1ZqKi1q4//ivsLPA44mSMFEa6X1XOW");    
    log.info("登录成功:{}",matches);
 }

三、自定义登录

前提:放行登录接口

流程:

  • 登录--调用验证方法--登录成功生成jwt--token存入redis
  • 调用验证方法:创建验证链--包装user和pwd--调用验证方法

 

配置类放行接口

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //设置拦截放行    
    @Override    
    protected void configure(HttpSecurity http) throws Exception {
        http    //关闭csrf                
                .csrf().disable()
                //不通过Session获取SecurityContext--认证之后的用户信息                
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //接口放行设置                
                .authorizeRequests()
                //对于登录接口 允许匿名访问 (SecurityContext没有值才能通过网关)                
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要网关权限认证                
                .anyRequest().authenticated();    
      }

    //注入验证链    
    @Bean    
    @Override    
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();    
    }

    //密码加密和解析    
    //创建BCryptPasswordEncoder注入容器    
    @Bean    
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();    
    }
}

接口调用方法

//自定义的登录验证
@Service
public class LoginServiceImpl implements LoginService {
    
    @Autowired    //验证链
    private AuthenticationManager authenticationManager;    
    
    @Autowired    //redfis
    StringRedisTemplate redisTemplate;    
    @Override    
    public String login(MyUser user) throws RemoteException {


        log.info("数据:{}",user);        
        //1、  把账号和密码进行包装--根据账号密码生成验证对象    
        UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(user.getName(),user.getPwd());                                        
        //验证方式是UserDetails中的验证方法,对封装的对象进行验证,如果验证成功会返回一个Authenticate对象
        Authentication authenticate = authenticationManager.authenticate(authenticationToken); // 把对象传入验证链中,这里验证链是自定义的       
        //2、 如果没通过,给出对应的提示,认证没同过就是空        
        if(Objects.isNull(authenticate)){
            throw new RemoteException("登录失败");        
         }


        //3、 如果认证通过了,使用userid生成一个jwt        
        //获取验证成功的对象   获取验证的成功的用户     
        LoginUser loginUser=(LoginUser) authenticate.getPrincipal();        
        String name = loginUser.getUser2().getName();        
        //TODO 生成jwt        
        //4、 TODO 把完整的用户信息存入redis ,userName作为key        
        redisTemplate.opsForValue().set(user.getName(), "token");        
        return "登录成功";    
    }
}

自定义登录验证链一环(上文解决问题实现)

//自定义的登录验证链
//实现UserDetailsService方法即可,返回一个用户对象
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired    
    private MyUserMapper userMapper;    
    @Override    
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
         // 查询用户信息        
         LambdaQueryWrapper<MyUser> queryWrapper=new LambdaQueryWrapper<>();       
          // 等值匹配        
          queryWrapper.eq(MyUser::getName,username);        
          MyUser user2=userMapper.selectOne(queryWrapper);        
          //如果没有查询结果 抛出异常        
          if(Objects.isNull(user2)){
            throw  new RuntimeException("用户不存在或者密码错误");        
            }


        //TODO 查询对应的权限信息        
        // 把数据封装成LoginUser(UserDetails)        
        return new LoginUser(user2);   
        
         }
}

四、定义JWT认证过滤器

简介

过滤拦截全部请求,配置在网关签名,当前jwt过滤器放行后才会进入网关验证

网关会读取存储在SecurityContexHolder 用户密码进行认证;

流程

 

 总结为:

定义jwt过滤器
    获取token
    解析token获取其中的userid
    从redis中获取用户信息
    存入SecurityContexHolder
    放行--进入网关验证

JWT(当前未使用)

是一种格式字符串(加密串),它允许设置过期时间

组成:

由三部分组成
    Header 头部--类型--固定
    Playload    负载--明文信息--自定义
    Signature  再加密

由  . 拼接

依赖:

  
<!--        jwt依赖-->        
<dependency>            
    <groupId>io.jsonwebtoken</groupId>            
    <artifactId>jjwt</artifactId>            
    <version>0.9.0</version>        
</dependency>

生成jwt

@Test
public void Test(){
    // 创建一个jwt对象    
    String jwtToken=Jwts.builder()
            // 1、Header头部--类型--固定            
            .setHeaderParam("typ","JWT") //类型 固定 是typ        
            .setHeaderParam("alg","HS256") //算法  固定      
            // 2、 Payloda: 负载--自定义信息--明文            
            .claim("username","1234") //自定义
            .setSubject("admin-test") //自定义
            .setExpiration(new Date(System.currentTimeMillis()+1000*50))// Toekn过期时间 ms            
            // id字段  可以存储 账号+uuid            
            .setId(UUID.randomUUID().toString())
            // 3、签名            
            .signWith(SignatureAlgorithm.HS256,"admin") //设置加密算法和--签名, 用于解密操作            
            .compact(); //使用.生成完整的字符串    
            
            log.info("JWT:{}",jwtToken);
            
        }

解析jwt

// 解析jwt
@Test
public void Test03(){
    // jwt串
    String token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjEyMzQiLCJzdWIiOiJhZG1pbi10ZXN0IiwiZXhwIjoxNjg4OTEwNDMyLCJqdGkiOiJlMjVkY2IwNy02MTc2LTQyY2ItYWYxOC02YWM3Mzg4YmM4MDMifQ.t4AqR4ni9UxCWuDXx07WQDJIv2CDadGXWR2pGOGi7Tk";    
    try {
            Jws<Claims> jwt=   Jwts.parser() //解析                
                                .setSigningKey("admin") //使用令牌 ,令牌要对应                
                                //类似 Map集合    
                                 // 将jwt转化成 key-value结构,如果过期了,那么将解密失败       
                                 // 获取jws对象中的数据      
                                .parseClaimsJws(token); 
                                  
                            log.info("数据:{}",jwt.getBody().getId());        
                             log.info("数据:{}",jwt);    
          }catch (Exception e){
                log.error("jwt失效");    
          }

}

定义token过滤

//过滤器
@Component
public class JwtAuthenticationTokenFiler extends OncePerRequestFilter {
    @Autowired    
    StringRedisTemplate redisTemplate;    
    
    //不拦截的请求---响应时候也会执行,但不会被干扰doFilterInternal
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        StringBuffer requestURL = request.getRequestURL();   //获取url
        String requestURI = request.getRequestURI();    //获取uri
        log.info("放行器当前请求:{}",requestURI); //但还是要先经过网关    
        if("/user/qt".equals(requestURI)){
            log.info("放行请求,{}",requestURL);        
            //不拦截 "/user/qt" 请求        
            return true;    
          }
       return false; //false 就会执行doFilterInternal
   }

    //拦截的请求
    @Override   
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        //1、--  获取token--        
        String token=request.getHeader("token");        
        if(!StringUtils.hasText(token)){
            //没有token就放行--放行到网关,没有账号密码网关不会被通过,所以当前请求无效           
               SecurityContextHolder.getContext().setAuthentication(null); //无token就清空 SecurityContextHolder域
             filterChain.doFilter(request,response);    //authenticationToken 为空将不会,放行后也不会执行后面的过滤链操作     
            return; //如果不return;后面响应返回来 还会继续执行        
            }
        //TODO   jwt解析token 获得获得账号        
        // 2、--  redis根据账号查询得到用户对象        
        String user = redisTemplate.opsForValue().get(token);        
        LoginUser loginUser= JSON.parseObject(user, LoginUser.class);        
        if(Objects.isNull(loginUser)){
            //应该不会执行这个--除非token非法            
            throw new RuntimeException("用户未登录");        
            }


        //3、--存入SecurityContextHolder--后面的操作都是从这里面获得信息的        
        // TODO 把数据封装到Authentication中,最后一个参数为为权限        
        UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(loginUser.getUsername(),loginUser.getPassword(),null);        
        SecurityContextHolder.getContext().setAuthentication(authenticationToken); //存放数据,//如果authenticationToken为null时候将不会执行后面的过滤连        
        //4、-- SecurityContextHolder携带账号信息放行到网关       
        filterChain.doFilter(request,response); //放行到下一个过滤器,无下一个过滤器所以直接到网关    }
}

配置过滤器为最前

配置在网关过滤前面

@Configurationpublic 
class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired //注入token过滤器    
    private JwtAuthenticationTokenFiler jwtAuthenticationTokenFiler;    
    //设置拦截放行    
    @Override    
    protected void configure(HttpSecurity http) throws Exception {
        http    //关闭csrf                
                .csrf().disable()
                //不通过Session获取SecurityContext--认证之后的用户信息                
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //接口放行设置                
                .authorizeRequests()
                //网关放行 登录接口              
                .antMatchers("/user/login").anonymous() //网关放行请求
                // 除上面外的所有请求全部需要权限认证                
                .anyRequest().authenticated();  //其他的请求网关都拦截    
                  
           // token过滤拦截配置在 网关拦截前面       
           http.addFilterBefore(jwtAuthenticationTokenFiler, UsernamePasswordAuthenticationFilter.class);    
                }

    //注入验证链    
    @Bean    
    @Override   
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();    
        }

    //密码加密和解析    
    //创建BCryptPasswordEncoder注入容器    
    @Bean    
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();    
     }
}

再次总结流程

请求-->token拦截(需要正确token才能通过)-->网关拦截(需要账号和密码才能通过)-->(过滤器逆顺序执行一遍)-->服务

        token拦截:需要配置shouldNotFilter方法配置不拦截的请求,拦截的请求放行后到达网关(SecurityContextHolder携带账号密码);
        网关拦截:在网关配置链中配置不拦截的请求,拦截的请求需要账号密码才会被放行。(默认会跳转一个登录页面);
        
    token拦截配置在网关拦截前面
        
登录请求--配置token放行--配置网关网关放行--自定义的登录验证(框架中提供了一个UserDetailsService验证接口,用AuthenticationManager执行验证链)---服务

五、退出登录

在token过滤器身份校验时候把信息从redis读取到了SecurityContexHolder中,只需要删除redis中存储的数据即可,

因为验证时候是先去redis中查找数据再存储到SecurityContexHolder中,可以根据SecurityContexHolder中的数据找到redis中的数据

实现服务

退出一般为登录状态,即拥有token

public void loginOut() throws RemoteException {
    //获得SecurityContexHolder中的用户id    
    UsernamePasswordAuthenticationToken user =(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();    
    // 或者
   // Authentication authentication = SecurityContextHolder.getContext().getAuthentication();   
    log.info( "authentication中的user数据:{}",authentication.getPrincipal());    
    //删除redis中的值    
    if(!Objects.isNull(user)){
        //不为空就删除数据        
        redisTemplate.opsForValue().getAndDelete((String) user.getPrincipal());        
        log.info("{},退出成功:",user.getPrincipal());       
        return;    
        }
        //没有数据
    throw new RemoteException("用户未登录");
}

六、一些中间配置

UserDetails 接口,继承它的类为验证数据User;

UsernamePasswordAuthenticationToken 类型中间变量,封装账号和密码
    principal:账号
    credentials:密码

Authentication:类型: 验证链验证的放回的结果
    principal: 实现UserDetails的User类,SecurityContextHolder.getContext()返回的Authentication 是账号
    credentials:密码
    Authorities :权限集合

AuthenticationManager 类型: 验证链

SecurityContextHolder.getContext():数据存放的位置,存放UsernamePasswordAuthenticationToken

七、网关的配置

链式配置
配置项目
authorizeRequests()
csrf()
formLogin()
httpBasic()
antMatcher()
等等
返回链式配置可以调用放回值为HttpSecurity的属性

禁用csrf保护
http.csrf().disable()

请求拦截:
authorizeRequests()
http.authorizeRequests().antMatchers("请求").anonymous() //SecurityContextHolder.getContext()中无数据才放行
http.authorizeRequests().antMatchers("请求").permitAll(); //直接放行当前请求,无论SecurityContextHolder.getContext() 是否有数据
.anyRequest().authenticated(); //(其他请求)认证后才可以访问

添加过滤器
http.addFilterBefore(jwtAuthenticationTokenFiler, UsernamePasswordAuthenticationFilter.class); //token验证在网关先执行

八、授权

权限控制(开启)

不同的用户使用不同的功能,必须具有所需权限才能进行相应的操作

配置类加入注解开启权限控制

@Configuration //配置类
//在配置类上加上当前注解,来表示开启当前权限认证功能
@EnableGlobalMethodSecurity(prePostEnabled = true) 

权限

@RequestMapping("/hello")
@PreAuthorize("hasAnyAuthority('test')") //是否有当前权限,必须有test权限才能访问

public String hello(){
    return "hello";
}

赋予用户权限

UserDetails添加权限字段属性

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {

    //账号密码   
    private MyUser user2;   
    //权限   
    private List<String> permissions;   
    
    @JSONField(serialize = false) //方法不进行序列化    
    @Override    
    public Collection<? extends GrantedAuthority> getAuthorities() { //放回权限信息      
        // 返回权限信息,封装成,SimpleGrantedAuthority对象,GrantedAuthority的实现类        
        List<SimpleGrantedAuthority> list =new ArrayList<>();        
        permissions.forEach(i->{
           SimpleGrantedAuthority simpleGrantedAuthority=new SimpleGrantedAuthority(i);            
            list.add(simpleGrantedAuthority);        
        });       
       return list;    
    }

    @JSONField(serialize = false)//方法不进行序列化    
    @Override    
    public String getPassword() {
        return  user2.getPwd().toString(); //获取用户密码    }

    @JSONField(serialize = false)//方法不进行序列化    
    @Override    
    public String getUsername() {
        return user2.getName();    }

    @JSONField(serialize = false)//方法不进行序列化    
    @Override    
    public boolean isAccountNonExpired() {
        return true; // 是否每过期    }

    @JSONField(serialize = false)//方法不进行序列化    
    @Override    
    public boolean isAccountNonLocked() {
        return true;    }

    @JSONField(serialize = false)//方法不进行序列化    
    @Override   
     public boolean isCredentialsNonExpired() {
        return true; //是否没有超时    }

    @JSONField(serialize = false)//方法不进行序列化    
    @Override    
    public boolean isEnabled() {
        return true; // 用户是否可用    }
}

从数据库查询权限信息

从数据库查询权限信息进行封装

RBAC权限模型:

基于角色的权限控制,这是目前最常被开发者使用也是相对易用、通用权限模型。

例如:

例如:
    用户表(小王,小红)
    角色表 (管理员,读者)
    关联表(小王-管理员,小王-读者,小红-读者)

用连接查表,三个表多表联查,查出权限

<select id="com.example.demo2.mapper.MeunMapper.getUserByUser" resultType="string">

    SELECT DISTINCT role_key   
     FROM user_role             
     left JOIN my_user  ON  user_role.myuser_id=my_user.id             
     LEFT JOIN role ON user_role.role_id=role.id    
     WHERE name=#{user};
 </select>

权限判断注解

hasAnyAuthority 方法可以传入多个权限,只要用户拥有任意一个权限就可访问

@PreAuthorize("hasAnyAuthority('a','b','c')") //拥有 a、b、c任意一个即可访问

自定义权限校验

使用权限校验

自定义方法

@Component("ex")//重命名
/** * 自定义权限校验 */
public class expression {
   public boolean hasAuthority(String authority){
       // 获取当前用户的权限 ,从域中获得用户信息     
       Authentication authentication = SecurityContextHolder.getContext().getAuthentication();       
       //存储 权限集合        
       List<String>list=new ArrayList<>();        
       // 获得权限集合        
       authentication.getAuthorities().forEach(i->{
            list.add(i.toString());        
        });        
        
            // 如果拥有对应权限        
       if(list.contains(authority)){
            return true;        
        }
       //当前权限不在权限集合中       
       return false;   
       }
}

使用自定义权限校验


 @PreAuthorize("@ex.hasAuthority('test')") //从Bean中找到ex类对象的hasAuthority方法
 @RequestMapping(value = "/user/qt",method = RequestMethod.POST)
 public String login() throws RemoteException {
     // 登录     
     log.info("qt执行成功");     
     return "qt"; 
}

九、自定义失败处理

我们还希望在认证失败或者授权失败的情况下也能和我们的接口一样返回相同的json,这样可以让前端能对响应进行统一的处理;

认证失败:
    如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntrypoint        
    对象方法去进行异常处理;

授权失败:
    如果是授权过程中出现异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法    
    去进行异常处理;

如果定义AuthenticationEntryPoint和AccessDeniedHandler然后配置给SpringSecurity即可

处理登录异常: 

@Slf4j
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {

    //处理认证异常    
    @Override    
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        //处理异常        
        log.info("当前请求:{} 认证失败",request.getRequestURL());        
        response.setContentType("application/json");//返回类型        
        response.setStatus(401);        
        //返回相应体        
        response.getWriter().println(JSON.toJSONString("用户认证失败,请重新登录")); //返回消息    
        }
}

权限不够异常:

/** *  处理权限不够的异常 */
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override    
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        //处理异常        
      

        log.info("当前请求:{} 权限不够",request.getRequestURL());        
        response.setContentType("application/json");//返回类型        
        response.setStatus(403);        
        //返回相应体        
        response.getWriter().println(JSON.toJSONString("您的权限不够")); 
        //返回消息    
       }
}

设置配置类中

// 配置异常处理器
http.exceptionHandling()
        //配置认证失败处理        
        .authenticationEntryPoint(authenticationEntryPoint)
        //配置权限不够处理        
        .accessDeniedHandler(accessDeniedHandler);

十、处理跨域请求

对Springboot配置跨域

配置类全局配置

@Configuration
//springboot设置跨域
public class CorsConfig implements WebMvcConfigurer {
    @Override    
    public void addCorsMappings(CorsRegistry registry) {
        //设置允许跨域的路径        
        registry.addMapping("/**")
                //设置允许跨域的请求域名                
                .allowedOriginPatterns("*")
                //是否允许cookie                
                .allowCredentials(true)
                // 设置允许的请求方式                
                .allowedMethods("GET","POST","DELETE","PUT")
                // 设置允许的header属性                
                .allowedHeaders("*")
                // 跨域允许的时间 --不再询问                
                .maxAge(3600);    
                }
}

@CrossOrigin注解局部方法配置(更加方便)

@CrossOrigin(origins = "http://localhost:8080") //允许当前访问
@RequestMapping(value = "/user/qt",method = RequestMethod.POST)
public String login() throws RemoteException {
    // 登录    log.info("qt执行成功");    
    return "qt";
    }

Spring Security配置允许跨域

配置类中设置如下:

http.cors(); //直接这个就表示允许跨域

十一、 CSRF

CSRF是指跨站请求伪造,是web常见的攻击之一

前后端分离需要关闭,不然会自动校验

 http    //关闭csrf
         .csrf().disable()

十二、总结

登录流程
    登录请求--token无数据放行--网关直接放行--登录接口--调用登录服务(登录部分使用验证链)
其他请求
    登录请求--token有数据放行--网关根据账号密码放行--服务
配置
    验证链:AuthenticationManager
    加密方式:PasswordEncoder
    过滤器:token过滤配置在网关验证前

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

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

相关文章

05-流式操作:使用 Flux 和 Mono 构建响应式数据流

1 通过 Flux 对象创建响应式流 基于各种工厂模式的静态创建方法编程的方式动态创建 Flux 相对而言&#xff0c;静态方法在使用上都比较简单&#xff0c;但不如动态方法来得灵活。我们来一起看一下。 2 通过静态方法创建 Flux Reactor 中静态创建 Flux 的方法常见的包括 jus…

机器学习、深度学习、人工智能三者之间究竟是什么关系?

1 什么是人工智能 人工智能&#xff08;Artificial Intelligence&#xff09;&#xff1a;人工智能是一个广泛的概念&#xff0c;指的是使计算机系统具备像人类一样的智能和能力。人工智能涵盖了包括机器学习和深度学习在内的各种方法和技术&#xff0c;旨在让计算机能够感知、…

IPO观察丨黑芝麻智能递表港交所,车芯行业已迈向量产决战期?

虽然我国汽车芯片行业还处在发展初期&#xff0c;但已经迸发出无限潜力。 一方面&#xff0c;智能汽车的发展不断带动需求增长&#xff1b;另一方面&#xff0c;政策引导下&#xff0c;汽车芯片企业持续抢占高地。对此&#xff0c;在7月5日-7日举办的2023年中国汽车论坛上&…

【数据结构二叉树OJ系列】7、构建二叉树并中序遍历

目录 题述&#xff1a; 思路&#xff1a; 正确代码&#xff1a; 题述&#xff1a; 编写一程序&#xff0c;读入用户输出的一串先序遍历字符串&#xff0c;根据此字符串建立一个二叉树&#xff08;以指针方式存储&#xff09;。例如如下的先序遍历字符串&#xff1a;ABC##DE#…

C++通过回车结束循环输入

试想一个案例&#xff0c;假设需要你输入n行数字&#xff0c;而每一行输入的数字数量都未知&#xff08;不定&#xff09;&#xff0c;如何通过C来实现这一操作&#xff1f; 本贴笔者给出一个具体案例&#xff1a;首先规定输入的行数&#xff0c;而后在每一行输入不定量的数字&…

Scratch 足球打蛤蟆

Scratch 足球打蛤蟆 本程序转换为HTML后运行。“足球”角色和角色“麦克斯”跟随鼠标&#xff0c;点击鼠标时“足球”角色复制并向鼠标方向开始移动&#xff0c;碰到边缘反弹&#xff0c;移动一定步数后删除。“蛤蟆”角色每0.5秒在随机位置和方向生成&#xff0c;碰到足球角色…

精彩回顾 | 模型与数据驱动工业数智化——Modelica暨装备数字化研讨会(2023)圆满召开

2023年6月30日&#xff0c;以“模型与数据驱动工业数智化”为主题的Modelica暨装备数字化研讨会&#xff08;2023&#xff09;在江苏省苏州市隆重召开。会议由苏州同元软控信息技术有限公司&#xff08;简称“同元软控”&#xff09;与哈尔滨工业大学计算学部等单位联合主办&am…

信息文档管理与配置管理

目录 ​编辑 一、软件文档的分类 1.1 开发文档 1.2 产品文档 1.3 管理文档 二、文档质量等级划分 2.1 1级文档 2.2 内部文档(2级) 2.3 工作文档&#xff08;3级&#xff09; 2.4 正式文档&#xff08;4级&#xff09; 三、配置管理 3.1 配置管理的定义 3.2 配置管理的6个主要…

F5是什么意思?聊聊你所不知道的F5

5月底&#xff0c;有幸参加了F5 Forum 科技趋势峰会&#xff0c;这让我不仅关注数字化企业和应用服务的技术趋势&#xff0c;也对华丽转型后的F5有了更深入的了解。如果你对F5是什么意思尚且存在疑问&#xff0c;那这篇文章我们就聊聊我眼中的F5&#xff0c;这个应用及API交付和…

编程(44)----------MySQL索引存储

MySQL的索引存储使用的并非像其展示出的那样以类似表格的方式. 而是以B数的方式存储. 在此之前先了解一下作为前身的B树 首先明确, 无论如何数据库中的存储方式都是树型. 而B树其实就是多叉树. 其结构大概如图所示. 假设根节点中存储了key1为5 33 这两个数据. 那其子节点就以这…

CYCLO(GLY-L-PHE),10125-07-2,环(甘氨酰-L-苯丙氨酰),具有生物活性的光学异构体

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ cyclo(Gly-Phe)&#xff0c;CYCLO(-GLY-PHE)&#xff0c;CYCLO(GLY-L-PHE)&#xff0c;Cyclo(-Gly-L-Phe)≥ 95% (HPLC) 环(甘氨酰-L-苯丙氨酰)Product structure&#xff1a; Product specifications&#xff1a; 1.CAS N…

软件测试工程师必备的SQL语句基础

文末有惊喜 为一个软件测试工程师&#xff0c;我们在测试过程中往往需要对数据库数据进行操作&#xff0c;但是我们的操作大多以查询居多&#xff0c;有时会涉及到新增&#xff0c;修改&#xff0c;删除等操作&#xff0c;所以我们其实并不需要对数据库的操作有特别深入的了解&…

vscode使用Eslint+Prettier格式化代码

1、安装Eslint插件和Prettier插件 2、 安装eslint npm install eslint -g1&#xff09;、初始化项目 npm init -y 2&#xff09;、生成eslint配置文件 npx eslint --init 完之后生成一个.eslintrc.json的文件 二、vscode配置 1、vscode需要配置保存自动化格式 设置 ->…

QWebEngine应用---基于QWebChannel实现网页与qt层交互

Qt提供了QWebChannel实现和网页的通信&#xff0c;我们直接拿github上一个能直接运行的demo来做说明&#xff0c;demo是基于Widget&#xff0c;且页面是自己实现的页面&#xff0c;接着会介绍基于QML实现且页面是第三方网站如何使用的。 QWebChannel用法 我们先看看demo的运行…

RocketMQ5.0消息消费<二> _ 消息队列负载均衡机制

RocketMQ5.0消息消费&#xff1c;二&#xff1e; _ 消息队列负载均衡机制 一、消费队列负载均衡概览 RocketMQ默认一个主题下有4个消费队列&#xff0c;集群模式下同一消费组内要求每个消费队列在同一时刻只能被一个消费者消费。那么集群模式下多个消费者是如何负载主题的多个…

第四十四章Java访问对象的属性和行为以及销毁

Java访问对象的属性和行为 每个对象都有自己的属性和行为&#xff0c;这些属性和行为在类中体现为成员变量和成员方法&#xff0c;其中成员变量对应对象的属性&#xff0c;成员方法对应对象的行为。 在Java中&#xff0c;要引用对象的属性和行为&#xff0c;需要使用点…

【MySQL 】MySQL 创建数据库, MySQL 删除数据库,MySQL 选择数据库

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

iPhone苹果手机桌面上快速记录笔记的步骤

现在越来越多的人喜欢上记笔记&#xff0c;因为记笔记是一种提升效率和组织思维的重要方式。随着移动设备的普及&#xff0c;手机逐渐成为我们生活中不可或缺的工具之一。在手机上记笔记就成为一种很快捷的记录方式&#xff0c;可以让我们随时随地记录灵感和重要信息。在众多记…

计算机体系结构基础知识介绍之动态调度Tomasulo 算法(二)

Tomasulo方法是一种计算机硬件架构的算法&#xff0c;用于动态调度指令的执行&#xff0c;允许乱序执行以及更有效率的使用多个执行单元。它由IBM公司在1967年提出&#xff0c;首次应用是在IBM System/360 Model 91的浮点单元上。Tomasulo方法的主要创新包括在硬件中进行寄存器…

轻松学习阿里云原生内存数据库Tair

&#x1f4d6;轻松学习阿里云原生内存数据库Tair &#x1f680;前言☁️什么是Redis&#xff1f;☁️什么是云原生内存数据库&#xff1f;✨特点 &#x1f680;阿里云原生内存数据库Tair&#x1f47b;简介✨功能特性&#x1f5fa;️应用场景 ✍️上手案例&#x1f3af; 基于Red…