java技术:spring-secrity实现认证、授权

目录

一、依赖

二、逻辑图

三、代码设计

1、WebSecurityConfigurerAdapter的实现类

2、设计登录接口

config配置:

1)UserDetailsService实现类重写:

2)书写登录实现类(调用authenticationManager、可以与后面的逻辑连贯起来)

四、认证加授权

五、工具类:(可网上随便找了用)

jwt

redis

redis序列化:

六、逻辑梳理

1)security有个Authentication

2)密码校验


一、依赖

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

二、逻辑图

图1

图2

三、代码设计

1、WebSecurityConfigurerAdapter的实现类

package com.example.config;import com.example.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** @author Mr.M* @version 1.0* @description 安全管理配置* @date 2022/9/26 20:53*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) //开启资源授权配置 权限和资源是两码事
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//    @Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;//配置用户信息服务
//    @Bean
//    public UserDetailsService userDetailsService() {
//        //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
//        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
//        return manager;
//    }
//@AutowiredJwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;//引入Bean AuthenticationManager 供调用@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic PasswordEncoder passwordEncoder() {
//        //密码为明文方式
//        return NoOpPasswordEncoder.getInstance()//spring用于加密的一个算法return new BCryptPasswordEncoder();}//配置安全拦截机制//__________________这里的所有路径都是不包含上下文路径的@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/login").anonymous()//放行接口 未登录才能访问.antMatchers().permitAll()//.anonymous()未登录 已登录不可访问//.authenticated()已登录//.permitAll()未登录已登录都可访问//接口权限配置.antMatchers("/getUser/*").hasAnyAuthority("getUser2").anyRequest().authenticated();//其它全部需要认证
//                .and()
//                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success//认证过滤器http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);//security允许跨域http.cors();}}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();
}

将AuthenticationManager注入bean 实现自己设计的登录接口与auth流程的衔接

   @Beanpublic PasswordEncoder passwordEncoder() {
//        //密码为明文方式
//        return NoOpPasswordEncoder.getInstance()//spring用于加密的一个算法return new BCryptPasswordEncoder();}

加密方式,如果不注入会在密码比对环节出问题

    @Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/**").anonymous()//放行接口.anyRequest().authenticated();//其它全部需要认证
//                .and()
//                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success//     http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);}

加载security前会加载的过滤器 可设置校验的接口匹配 这里的.antMatchers("/**").anonymous()时放行所有 既所有接口都会进入auth校验

ps:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/r/**").authenticated()//访问/r开始的请求需要认证通过
                .anyRequest().permitAll()//其它请求全部放行
                .and()
                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
    }
 

2、设计登录接口

改写两部分 一部分是UserDetailsService(默认是从内存传用户名密码到UserDetails对象)

改成:自己设计UserDetailsService的实现类从数据库加载

二部分是,重写的登录接口,不用默认的(返回JWT令牌,并将token存入redis缓存)

ps:token的claim最好用普通字段 不然可能会出现实体类解析失败 这里存userId(因为redis序列化会破坏实体类、序列化最好是List<简单类型>)

config配置:

package com.example.config;import com.example.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** @author Mr.M* @version 1.0* @description 安全管理配置* @date 2022/9/26 20:53*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) //开启资源授权配置 权限和资源是两码事
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//    @Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;//配置用户信息服务
//    @Bean
//    public UserDetailsService userDetailsService() {
//        //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
//        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
//        return manager;
//    }
//@AutowiredJwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;//引入Bean AuthenticationManager 供调用@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic PasswordEncoder passwordEncoder() {
//        //密码为明文方式
//        return NoOpPasswordEncoder.getInstance()//spring用于加密的一个算法return new BCryptPasswordEncoder();}//配置安全拦截机制//__________________这里的所有路径都是不包含上下文路径的@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/login").anonymous()//放行接口 未登录才能访问.antMatchers().permitAll()//.anonymous()未登录 已登录不可访问//.authenticated()已登录//.permitAll()未登录已登录都可访问//接口权限配置.antMatchers("/getUser/*").hasAnyAuthority("getUser2").anyRequest().authenticated();//其它全部需要认证
//                .and()
//                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success//认证过滤器http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);//security允许跨域http.cors();}}

ps:配置有过滤对象,授权权限、跨域(spring跨域+security跨域)

spring跨域配置

package com.example.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.FormContentFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author Mr.Lan* @version 1.0* @ClassName CrosConfig$* @description Springboot跨域配置 等同nacos配置(如网关) 但也需要SpringSecurity配置* @date 2024/5/19 0:32**/
@Configuration
public class CrosConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedHeaders("Content-Type","X-Requested-With","accept,Origin","Access-Control-Request-Method","Access-Control-Request-Headers","token").allowedMethods("*").allowedOrigins("*").allowCredentials(true);}/*** 支持PUT、DELETE请求*/@Beanpublic FormContentFilter httpPutFormContentFilter() {return new FormContentFilter();}}
1)UserDetailsService实现类重写:
package com.example.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.entity.LoginUser;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.util.ObjectUtils;import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;/*** @author Mr.Lan* @version 1.0* @ClassName UserDetailsServiceImpl$* @description TODO* @date 2024/5/17 15:52**/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@AutowiredUserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {//传参是用户姓名//数据库查询用户信息以及权限信息LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>();userLambdaQueryWrapper.eq(User::getUsername,s);User user = userMapper.selectOne(userLambdaQueryWrapper);if( ObjectUtils.isEmpty(user)){throw new RuntimeException("用户不存在");}//查询权限信息ArrayList<String> permissions = new ArrayList<>(Arrays.asList("getUser", "getUser1"));LoginUser loginUser = new LoginUser(user,permissions);//返回UserDeatil对象//返回接口的实现类相当于返回了接口return loginUser;//返回后后面会校验密码}
}

ps:对于封装成UserDetails接口返回:可以返回实现类(接口的实例)

两种情况:

第一种 直接new实现类 然后将自己的参数传进去封装起来

第二种  创建实现类 将自己的参数与实现类联系起来

这里是第二种:

package com.example.entity;import com.alibaba.fastjson.annotation.JSONField;
import com.example.entity.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;/*** @author Mr.Lan* @version 1.0* @ClassName LoginUser$* @description LoginUser充当中间人实现UserDetails与User的关系* @date 2024/5/17 16:10**/
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {private User user;private ArrayList<String> permisssions;@JSONField(serialize = false)private List<SimpleGrantedAuthority> permisssionsList;public User getUser() {return user;}public LoginUser(User user, ArrayList<String> permisssions) {this.user = user;this.permisssions = permisssions;}public LoginUser(User user) {this.user = user;}public void setUser(User user) {this.user = user;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {
//        List<GrantedAuthority> collect = new ArrayList<>();
//        for (String permisssion : this.permisssions) {
//            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permisssion);
//            collect.add(simpleGrantedAuthority);
//        }
//        List<GrantedAuthority> collect = this.permisssions.stream().map(permisssion -> {
//            return new SimpleGrantedAuthority(permisssion);
//        }).collect(Collectors.toList());if (permisssionsList != null) {
//            return this.permisssionsList;//加this表示当前对象的属性 不加表示这个类中字段的值return permisssionsList;}this.permisssionsList = this.permisssions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return permisssionsList;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUsername();}//是否有效(没过期)@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

2)书写登录实现类(调用authenticationManager、可以与后面的逻辑连贯起来)
package com.example.service.impl;import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.MapUtils;
import com.alibaba.spring.util.ObjectUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.LoginUser;
import com.example.entity.Result;
import com.example.entity.User;
import com.example.service.UserService;
import com.example.mapper.UserMapper;
import com.example.utils.RedisTemplateUtils;
import io.jsonwebtoken.Claims;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapProperties;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;import javax.xml.crypto.dsig.keyinfo.RetrievalMethod;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;import static com.example.utils.JwtUtils.generateJwt;/**
* @author Admin
* @description 针对表【user】的数据库操作Service实现
* @createDate 2024-05-16 21:03:01
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService{@AutowiredAuthenticationManager authenticationManager;@AutowiredUserMapper userMapper;@AutowiredRedisTemplateUtils redisTemplateUtils;//authenticationManager在login的方法(这里是实现类)调用,就继续传递@Overridepublic Result login(User user) {//当参数是接口时可以传接口的实现类 创建实现类封装传递 Authentication//new UsernamePasswordAuthenticationToken()的两个参数 Object 后面要用UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());//这里将返回的时认证后的结果Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);if(Objects.isNull(authenticate)){throw new RuntimeException("认证失败");}BeanUtils.copyProperties(authenticate.getPrincipal(),user);//获取返回中的信息 加密返回tokenHashMap<String, Object> claims = new HashMap<>();LoginUser principal = (LoginUser)authenticate.getPrincipal();claims.put("userId",principal.getUser().getId());String token = generateJwt(claims);//将token存入redis 并以userId为keyredisTemplateUtils.set("login:"+principal.getUser().getId(),principal,432000L);HashMap<String, String> map = new HashMap<>();map.put("token",token);
//        String jsonString = JSON.toJSONString(map);return Result.success(400,"登陆成功",map);}@Overridepublic Result loginOut() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();LoginUser principal = (LoginUser)authentication.getPrincipal();String userId = principal.getUser().getId();//删除redis中的tokentry {redisTemplateUtils.del("login:"+userId);} catch (Exception e) {e.printStackTrace();return Result.error("退出登录失败");}return Result.success("成功退出登录");}
}

登录接口:

package com.example.web;import com.example.entity.Result;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import com.example.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.omg.PortableInterceptor.USER_EXCEPTION;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;import static com.example.utils.JwtUtils.generateJwt;/*** @author Mr.Lan* @version 1.0* @ClassName LoginController$* @description TODO* @date 2024/5/16 21:05**/
@RestController
@Slf4j
public class LoginController {@AutowiredUserService userService;@GetMapping("/getUser/{id}")
//    @PreAuthorize("hasAuthority('getUser2')")//需要权限才能访问User getUser(@PathVariable Long id){User user = userService.getById(id);log.info("User:{}",user);return user;}@RequestMapping("/login-success")public String loginSuccess() {return "登录成功";}
//    @GetMapping("/login-success")
//    public String loginSuccess() {
//
//        return "登录成功";
//    }@RequestMapping("/generateJwt")public String Jwt() {HashMap<String,Object> claim=new HashMap<String,Object>();claim.put("name","admin");claim.put("password","admin");String token = generateJwt(claim);return token;}@PostMapping("/login")public Result login(@RequestBody User user) {return userService.login(user);}@GetMapping("/loginOut")public Result loginOut() {return userService.loginOut();}}

四、认证加授权

在配置类中将该过滤器置于UsernamePasswordAuthenticationFilter过滤器之前

package com.example.filter;import com.example.entity.LoginUser;
import com.example.utils.JwtUtils;
import com.example.utils.RedisTemplateUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.checkerframework.checker.units.qual.C;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.rmi.server.ExportException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;import static com.example.utils.JwtUtils.parseJWT;/*** @author Mr.Lan* @version 1.0* @ClassName JwtAuthenticationTokenFilter$* @description token中存用户信息 方便获取 提高查询效率 完成授权 而tJWT就是通过userId校验* @date 2024/5/17 21:27**/
@Slf4j
@Component
//@WebFilter(urlPatterns = "/*")
public class JwtAuthenticationTokenFilter implements Filter {@AutowiredRedisTemplateUtils redisTemplateUtils;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("初始化");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest Request, ServletResponse Response, FilterChain filterChain) throws ServletException, IOException {HttpServletRequest servletRequest = (HttpServletRequest) Request;HttpServletResponse servletResponse = (HttpServletResponse) Response;//获取请求头的tokenString token = servletRequest.getHeader("token");if(StringUtils.isEmpty(token)){//未登录放行filterChain.doFilter(Request,Response);}Map<String, Object> claims=null;try {claims = parseJWT(token);} catch (Exception e) {throw new RuntimeException("token异常");}String userId = claims.get("userId").toString();String key="login:"+userId;//获取缓存的key看是否过期关键快速是获取用户的一些信息//一般认证和授权分开的 授权的token从redis获取LoginUser loginUser = (LoginUser)redisTemplateUtils.get(key);if(Objects.isNull(loginUser)){throw new RuntimeException("token非法");}//authorities存认证信息//todo LoginUser保护用户信息和权限信息UsernamePasswordAuthenticationToken info = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());SecurityContextHolder.getContext().setAuthentication(info);//已登录放行filterChain.doFilter(Request,Response);}@Overridepublic void destroy() {log.info("结束");Filter.super.destroy();}
}

ps:在封装SecurityContextHolder中加入权限信息,后面的FilterSecurityInterceptor会根据接口所需权限进行拦截

权限可用注解或者配置类中配置

五、工具类:(可网上随便找了用)

jwt

package com.example.utils;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import java.util.Date;
import java.util.Map;/*** @author Mr.Lan* @version 1.0* @ClassName JwtUUtils$* @description TODO* @date 2024/5/17 12:30**/
public class JwtUtils {private static String signKey = "lanjie";//签名密钥private static Long expire = 43200000L; //有效时间/*** 生成JWT令牌* @param claims JWT第二部分负载 payload 中存储的内容* @return*/public static String generateJwt(Map<String, Object> claims){String jwt = Jwts.builder().addClaims(claims)//自定义信息(有效载荷).signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部).setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间.compact();return jwt;}/*** 解析JWT令牌* @param jwt JWT令牌* @return JWT第二部分负载 payload 中存储的内容*/public static Claims parseJWT(String jwt){Claims claims = Jwts.parser().setSigningKey(signKey)//指定签名密钥.parseClaimsJws(jwt)//指定令牌Token.getBody();return claims;}
}

redis

package com.example.utils;import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import lombok.extern.slf4j.Slf4j;import javax.annotation.Resource;/*** @author 陈振东* 基于spring和redis的redisTemplate工具类 针对所有的hash 都是以h开头的方法 针对所有的Set 都是以s开头的方法 不含通用方法 针对所有的List 都是以l开头的方法*/
@Component
@Slf4j
public class RedisTemplateUtils {@Resourceprivate RedisTemplate<String, Object> redisTemplate;// =============================1-common============================/*** 指定缓存失效时间** @param key  键* @param time 时间(秒)* @return*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 根据key 获取过期时间** @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {log.error(key, e);return false;}}/*** 删除缓存** @param key 可以传一个值 或多个*/@SuppressWarnings("unchecked")public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(key));}}}// ============================2-String=============================/*** 普通缓存获取** @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** @param key   键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 普通缓存放入并设置时间** @param key   键* @param value 值* @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {log.error(key, e);return false;}}//    /**
//     * 递增 适用场景: https://blog.csdn.net/y_y_y_k_k_k_k/article/details/79218254 高并发生成订单号,秒杀类的业务逻辑等。。
//     *
//     * @param key 键
//     * @param   要增加几(大于0)
//     * @return
//     */public long incr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递增因子必须大于0");}return redisTemplate.opsForValue().increment(key, delta);}//    /**
//     * 递减
//     *
//     * @param key 键
//     * @param by  要减少几(小于0)
//     * @return
//     */public long decr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递减因子必须大于0");}return redisTemplate.opsForValue().increment(key, -delta);}// ================================3-Map=================================/*** HashGet** @param key  键 不能为null* @param item 项 不能为null* @return 值*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值** @param key 键* @return 对应的多个键值*/public Map<Object, Object> hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet** @param key 键* @param map 对应多个键值* @return true 成功 false 失败*/public boolean hmset(String key, Map<String, Object> map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {log.error(key, e);return false;}}/*** HashSet 并设置时间** @param key  键* @param map  对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean hmset(String key, Map<String, Object> map, long time) {try {redisTemplate.opsForHash().putAll(key, map);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key   键* @param item  项* @param value 值* @return true 成功 false失败*/public boolean hset(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key   键* @param item  项* @param value 值* @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败*/public boolean hset(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 删除hash表中的值** @param key  键 不能为null* @param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item) {redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值** @param key  键 不能为null* @param item 项 不能为null* @return true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** @param key  键* @param item 项* @param by   要增加几(大于0)* @return*/public double hincr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** @param key  键* @param item 项* @param by   要减少记(小于0)* @return*/public double hdecr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, -by);}// ============================4-set=============================/*** 根据key获取Set中的所有值** @param key 键* @return*/public Set<Object> sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {log.error(key, e);return null;}}/*** 根据value从一个set中查询,是否存在** @param key   键* @param value 值* @return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {log.error(key, e);return false;}}/*** 将数据放入set缓存** @param key    键* @param values 值 可以是多个* @return 成功个数*/public long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {log.error(key, e);return 0;}}/*** 将set数据放入缓存** @param key    键* @param time   时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long sSetAndTime(String key, long time, Object... values) {try {Long count = redisTemplate.opsForSet().add(key, values);if (time > 0)expire(key, time);return count;} catch (Exception e) {log.error(key, e);return 0;}}/*** 获取set缓存的长度** @param key 键* @return*/public long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {log.error(key, e);return 0;}}/*** 移除值为value的** @param key    键* @param values 值 可以是多个* @return 移除的个数*/public long setRemove(String key, Object... values) {try {Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {log.error(key, e);return 0;}}// ============================5-zset=============================/*** 根据key获取Set中的所有值** @param key 键* @return*/public Set<Object> zSGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {log.error(key, e);return null;}}/*** 根据value从一个set中查询,是否存在** @param key   键* @param value 值* @return true 存在 false不存在*/public boolean zSHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {log.error(key, e);return false;}}public Boolean zSSet(String key, Object value, double score) {try {return redisTemplate.opsForZSet().add(key, value, 2);} catch (Exception e) {log.error(key, e);return false;}}/*** 将set数据放入缓存** @param key    键* @param time   时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long zSSetAndTime(String key, long time, Object... values) {try {Long count = redisTemplate.opsForSet().add(key, values);if (time > 0)expire(key, time);return count;} catch (Exception e) {log.error(key, e);return 0;}}/*** 获取set缓存的长度** @param key 键* @return*/public long zSGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {log.error(key, e);return 0;}}/*** 移除值为value的** @param key    键* @param values 值 可以是多个* @return 移除的个数*/public long zSetRemove(String key, Object... values) {try {Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {log.error(key, e);return 0;}}// ===============================6-list=================================/*** 获取list缓存的内容** @param key   键* @param start 开始 0 是第一个元素* @param end   结束 -1代表所有值* @return* @取出来的元素 总数 end-start+1*/public List<Object> lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {log.error(key, e);return null;}}/*** 获取list缓存的长度** @param key 键* @return*/public long lGetListSize(String key) {try {return redisTemplate.opsForList().size(key);} catch (Exception e) {log.error(key, e);return 0;}}/*** 通过索引 获取list中的值** @param key   键* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推* @return*/public Object lGetIndex(String key, long index) {try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {log.error(key, e);return null;}}
//
//    /**
//     * 将list放入缓存
//     *
//     * @param key   键
//     * @param value 值
//     * @param time  时间(秒)
//     * @return
//     */public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, Object value, long time) {try {redisTemplate.opsForList().rightPush(key, value);if (time > 0)expire(key, time);return true;} catch (Exception e) {log.error(key, e);return false;}}//    /**
//     * 将list放入缓存
//     *
//     * @param key   键
//     * @param value 值
//     * @param time  时间(秒)
//     * @return
//     */public boolean lSet(String key, List<Object> value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, List<Object> value, long time) {try {redisTemplate.opsForList().rightPushAll(key, value);if (time > 0)expire(key, time);return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 根据索引修改list中的某条数据** @param key   键* @param index 索引* @param value 值* @return*/public boolean lUpdateIndex(String key, long index, Object value) {try {redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception e) {log.error(key, e);return false;}}/*** 移除N个值为value** @param key   键* @param count 移除多少个* @param value 值* @return 移除的个数*/public long lRemove(String key, long count, Object value) {try {Long remove = redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {log.error(key, e);return 0;}}}

redis序列化:

package com.example.config;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.util.IOUtils;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;/*** {@link RedisSerializer} FastJson Generic Impl* @author lihengming* @since 1.2.36*/
public class RedisSerialization implements RedisSerializer<Object> {private final static ParserConfig defaultRedisConfig = new ParserConfig();static { defaultRedisConfig.setAutoTypeSupport(true);}public byte[] serialize(Object object) throws SerializationException {if (object == null) {return new byte[0];}try {return JSON.toJSONBytes(object, SerializerFeature.WriteClassName);} catch (Exception ex) {throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);}}public Object deserialize(byte[] bytes) throws SerializationException {if (bytes == null || bytes.length == 0) {return null;}try {return JSON.parseObject(new String(bytes, IOUtils.UTF8), Object.class, defaultRedisConfig);} catch (Exception ex) {throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);}}
}

六、逻辑梳理

这里用的springSecurity实现了用户的登录、认证、授权

1、登录

自定义登录接口实现了第一层的过滤器,包括了密码校验,以及拥有的权限

登录是通过改写过滤器,

 利用authenticationManager.authenticate(usernamePasswordAuthenticationToken);

引入authenticate串联起来整个secrity登录过程

2、认证

认证接口实现,首先认证是通过新增过滤器(同时加权限)。对token进行校验(是否有效以及是否过期等)并设置security上下文

下面介绍两个变量

1)security的上下文 SecurityContextHolder(存放用户的账户、密码、权限信息)

存:
UsernamePasswordAuthenticationToken info = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(info);

1、存入两个参数(一般用户名密码)

2、存三个(前面两个自定义,后面的是授权信息)

取:

通过Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

其中SecurityContextHolder 是 Spring Security 框架提供的一个重要的类,用于保存和获取当前请求的安全上下文信息。

2)密码校验  UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());

这里存入输入的用户名和密码

这里输入的是数据库用户 但是在loginUser中重写了后去用户名密码是从User中获取 因此相当于传入了用户名密码,后面会在图2的第七步校验用户名密码。

ps:oauth2协议可以更加方便的实现认证授权以及分布式认证授权。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重新夺回控制权!原创始人从Synk回购FossID,致力于解决开源许可合规风险

FossID 于 2022 年 9 月被其原始创始人从 Snyk, Inc. 重新收购。为什么 Snyk 在 2021 年收购了 FossID&#xff0c;又在 2022 年将其分拆&#xff0c;以及为什么 FossID 的创始人&#xff08;Oskar Swirtun 和 Jon Aldama&#xff09;后来又回购了该公司&#xff1f; 公司背景 …

类和对象的基本概念

类和对象的基本概念 C和C中struct区别类的封装封装访问权限总结struct和class的区别 将成员变量设置为private C和C中struct区别 C语言struct只有变量C语言struct 既有变量&#xff0c;也有函数 类的封装 封装 把变量&#xff08;属性&#xff09;和函数&#xff08;操作&a…

Undet for SketchUp 2023.3 点云建模软件 支持支持草图大师sketchup2021-2022-2023

1.Undet for sketchup 2023.3支持草图大师sketchup2021-2022-2023。支持机载雷达扫描、车载扫描还是地面扫描&#xff0c;对AEC行业用户来说&#xff0c;真正需要的是如何将这些数据快速处理为三维模型&#xff0c;这样才能将这些信息延展到BIM领域发挥效用。因此面对这些海量的…

Go源码--sync库(1)sync.Once和

简介 这篇主要介绍 sync.Once、sync.WaitGroup和sync.Mutex sync.Once once 顾名思义 只执行一次 废话不说 我们看源码 英文介绍直接略过了 感兴趣的建议读一读 获益匪浅 其结构体如下 Once 是一个严格只执行一次的object type Once struct {// 建议看下源码的注解&#xf…

python:pycharm虚拟解释器报错环境位置目录为空

目录 解释器分控制台解释器 和 pycharm解释器 控制台解释器切换&#xff1a; pycharm解释器 解释器分控制台解释器 和 pycharm解释器 控制台解释器切换&#xff1a; 切换到解释器下 激活解释器 查看解释器 where python 激活成功 这时在控制台使用python xxx.py 可以…

表现层框架设计之表现层设计模式_3.MVVM模式

1.MVVM模式 MVVM模式正是为解决MVP中UI种类变多&#xff0c;接口也会不断增加的问题而提出的。 MVVM模式全称是模型-视图-视图模型&#xff08;Model-View-ViewModel&#xff09;&#xff0c;它和MVC、MVP类似&#xff0c;主要目的都是为了实现视图和模型的分离&#xff0c;不…

Flink 生态对 Confluent / Kafka Schema Registry 支持情况的研究报告

文章目录 1. Flink CDC 对 Confluent Schema Registry 的支持情况2. Confluent Avro Format 对 Confluent Schema Registry 的支持情况3. 关键性结论 这几年&#xff0c;在流式链路上引入一个 Schema Registry 变得越来越流行&#xff0c;也越来越有必要&#xff0c; Schema Re…

信息化项目交付验收流程管理办法

项目交付验收流程制度 管理办法 (执行版) (文件编号: ) 编制: 审核: 批准: 版本: 生效日期: 管理办法概述 制定目的为了保证公司在建项目交付验收工作事项的顺利开展,保证交付验收进度及…

舵机(结构,原理,控制方法)

介绍 舵机&#xff0c;全称为伺服马达&#xff08;Servo Motor&#xff09;&#xff0c;是一种能够精确控制角度或位置的电动机。它广泛应用于模型制作、机器人技术、工业自动化等领域。舵机通过接收控制信号&#xff0c;将其转化为机械运动&#xff0c;从而实现精确的控制。 …

211初试自命题复试线仅302分!延边大学计算机考研考情分析!

延边大学&#xff08;Yanbian University&#xff09;&#xff0c;简称“延大”&#xff0c;地处吉林省延边朝鲜族自治州&#xff0c;是国家“双一流”建设高校、国家“211工程”重点建设大学、西部开发重点建设院校、吉林省人民政府和教育部共同重点支持建设大学、吉林省人民政…

pcd点云江湖之处处碰壁:点云文件pcd加载02

江湖好汉&#xff0c;休走&#xff0c;废了半天力气把threejs自带的代码搬迁到自己项目中了&#xff0c;高高兴兴给领导看。领导一句话&#xff0c;顿时无奈&#xff1a;领导曰&#xff1a;点云单色太丑&#xff0c;能不能按照分类展示&#xff1f; 一句话难道英雄好汉&#xf…

IT廉连看——UniApp——事件绑定

IT廉连看——UniApp——事件绑定 这是我们上节课最终的样式&#xff1b; 一、现在我有这样一个需求&#xff0c;当我点击“生在国旗下&#xff0c;长在春风里”它的颜色由红色变为蓝色&#xff0c;该怎么操作&#xff1f; 这时候我们需要一个事件的绑定&#xff0c;绑定一个单…

【webrtc】m98:Call的创建及Call对音频接收处理

call中多個流共享相同的辅助组件 这几个是与外部共用的 线程传输send控制module 线程任务队列工厂call的辅助组件中各种统计以及接收测的cc是自己创建的 call自己的多个辅助组件是外部传递来的 call 创建多个接收流 这里用一个set 来保存所有指针,并没有要map的意思:

debian nginx upsync consul 实现动态负载

1. consul 安装 wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg echo "deb [signed-by/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_r…

本是梦中人,常作花下客。心中自往来,知我有几个。

我们总是喜欢拿“顺其自然”来敷衍人生道路上的荆棘坎坷&#xff0c;却很少承认&#xff0c;真正的顺其自然&#xff0c; 其实是竭尽所能之后的不强求&#xff0c; 而非两手一摊的不作为。 一花凋零荒芜不了整个春天&#xff0c; 一次挫折也荒废不了整个人生。 多年后&#x…

GQL 来了!ISO/IEC 正式发布 GQL 数据库国际标准!

历时四年筹备&#xff0c;超过20个国家的标准和技术专家参与制定&#xff0c;ISO/IEC GQL &#xff08;图查询语言&#xff09;标准于2024年4月12日正式发布&#xff01; 作为国际标准化组织&#xff08;ISO&#xff09;继 1987年 发布SQL后&#xff0c;唯一发布的数据库查询语…

【数据结构】哈夫曼树和哈夫曼编码

一、哈夫曼树 1.1 哈夫曼树的概念 给定一个序列&#xff0c;将序列中的所有元素作为叶子节点构建一棵二叉树&#xff0c;并使这棵树的带权路径长度最小&#xff0c;那么我们就得到了一棵哈夫曼树&#xff08;又称最优二叉树&#xff09; 接下来是名词解释&#xff1a; 权&a…

Vue 3 的 setup语法糖工作原理

前言 我们每天写vue3项目的时候都会使用setup语法糖&#xff0c;但是你有没有思考过下面几个问题。setup语法糖经过编译后是什么样子的&#xff1f;为什么在setup顶层定义的变量可以在template中可以直接使用&#xff1f;为什么import一个组件后就可以直接使用&#xff0c;无需…

SpringBoot+layuimini实现角色权限菜单增删改查(layui扩展组件 dtree)

角色菜单 相关组件方法效果图MySQL代码实现资源菜单树组件实现权限树方法js这里我先主要实现权限树的整体实现方法&#xff0c;如果是直接查看使用的话可以只看这里&#xff01; 后端代码Controlle层代码Service代码及实现类代码Service代码ServiceImpl代码 resourceMapper 代码…