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.mzph.cn/news/840122.shtml

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

相关文章

doxygen 1.11.0 使用详解(十)——包含表格

目录 Doxygen supports two ways to put tables in the documentation. The easiest is to use the Markdown format as shown in Markdown Extensions section Tables. Although this format is easy to use and read, it is also rather limited. It supports only a simpl…

Vue学习笔记2——创建一个Vue项目

Vue项目 1、创建一个Vue项目2、Vue项目的目录结构3、模版语法4、属性绑定5、条件渲染 1、创建一个Vue项目 vue官方文档&#xff1a; https://cn.vuejs.org/打开命令行界面&#xff08; “winR"再输入"cmd”&#xff09;&#xff0c;切换位置到指定的位置创建vue项目…

Oracle 归档模式学习笔记

版权声明&#xff1a;本文为CSDN博主「杨群」的原创文章&#xff0c;遵循CC 4.0 BY-SA版权协议&#xff0c;于2024年5月17日首发于CSDN&#xff0c;转载请附上原文出处链接及本声明。   原文链接&#xff1a;https://blog.csdn.net/u011046671/article/details/139012428 一…

微星打造全球首款DDR5 CAMM2内存主板:这下清爽了

微星预告了新款主板“ Z790 Project Zero Plus”&#xff0c;这也是 全球第一款配备DDR5 CAMM2内存的桌面主板&#xff01; CAMM全称为“Compression Attached Memory Module”&#xff0c;已被JEDEC组织接纳为行业标准&#xff0c;相比传统的SO-DIMM、DIMM内存最大特点就是取消…

MM模块五(采购订单)

创建采购订单&#xff1a;ME21N 参考报价单创建采购订单&#xff1a; 哪个采购部门的哪个采购员为哪个公司代码下的采购订单 点击保存 注&#xff1a;采购订单的数量不能大于采购申请的数量

Milvus 高级特性

引言 Milvus 的高级特性包括分布式架构、安全性和监控与日志管理&#xff0c;这些特性使得 Milvus 能够更好地适应企业级应用的需求。 一、分布式架构 1.1 集群部署 在当今大数据和人工智能的时代&#xff0c;向量数据库以其对高维数据的强大处理能力&#xff0c;成为了许多…

Map遍历、反射、GC

map的遍历 用foreach遍历 HashMap<Character,Integer> map new HashMap<>();map.put(A,2);map.put(B,3);map.put(C,3);for (Map.Entry<Character,Integer> entry: map.entrySet()) {char key entry.getKey();int value entry.getValue();System.out.prin…

影视解说5.0版零基础视频课程

课程简介 现在还能做解说吗、不会写解说文案怎么解决、不会配音怎么解决、如何找到合适的素材资源、如何变现…这是很多想做解说的伙伴最关心的几大问题。比如文案&#xff0c;我们推荐一个网站&#xff0c;10分钟搞定一篇文案&#xff0c;配音可以真人配音也可以软件配音。5.…

软考-2023年11月系统架构设计师真题回忆

2023年11月系统架构设计师真题回忆 选择题 1-2.Mccabe 度量法&#xff08;错误的是V(g&#xff09;m-n1、最大范围≤10) 3.多线程任务通信方式&#xff08;共享内存消息传递&#xff09; 4.星型拓扑结构最多2跳 5.数据库(A->B,B->C),属于2NF 6.数据库去除多值依赖属…

【区块链】fisco网络运维之添加节点黑名单

基于已完成的区块链系统与管理平台搭建工作&#xff0c;开展区块链节点的黑名单工作&#xff0c;具体操作如下 以node3为例子 1查看node0节点的连接状态日志&#xff08;现有4个节点连接&#xff09; 注意&#xff1a;如果查询不到连接状态&#xff0c;修改node0的配置文件中…

DataFrame—数据汇总4

文章最前&#xff1a; 我是Octopus&#xff0c;这个名字来源于我的中文名--章鱼&#xff1b;我热爱编程、热爱算法、热爱开源。所有源码在我的个人github &#xff1b;这博客是记录我学习的点点滴滴&#xff0c;如果您对 Python、Java、AI、算法有兴趣&#xff0c;可以关注我的…

Flask多线程开发指南

文章目录 1. 什么是多线程&#xff1f;2. Flask中的多线程3. 注意事项结论 在Web应用程序开发中&#xff0c;有时候需要处理一些耗时的任务&#xff0c;例如与数据库交互、发送网络请求或执行计算密集型的操作。为了保持用户体验的流畅性&#xff0c;我们可以使用多线程来处理这…

LoadBalancer

一、手写随机负载均衡 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency><!--引入nacos discovery--> <dependency><groupId>com…

socket地址理解

socket介绍 套接字的基本概念 1. 套接字的定义&#xff1a; 套接字&#xff08;socket&#xff09;是计算机网络中用于通信的端点&#xff0c;它抽象了不同主机上应用进程之间双向通信的机制。 2. 套接字的作用&#xff1a; 套接字连接应用进程与网络协议栈&#xff0c;使…

vb.net打开CAD指指定路径文件

首先打开vsto,创建窗体&#xff0c;添加一个按钮&#xff0c;双击按钮录入代码&#xff1a; Public Class Form1Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.ClickDim cad As Objectcad CreateObject("autocad.Application")cad…

Java枚举的本质

目录 1.枚举简介 1.1.规范 1.2.枚举类真实的样子 1.3.枚举类的特点 1.4.枚举可以使用的方法 1.4.1.toString()方法 1.4.2.valueOf方法 1.4.3.values方法 1.4.4.ordinal方法 1.5.枚举的用法 1.5.1.常量 1.5.2.switch 1.5.3.枚举中增加方法 1.5.4.覆盖枚举方法 1.5…

工具推荐:市面上有哪些带有ai问答机器人的SaaS软件

众所周知&#xff0c;SaaS&#xff08;软件即服务&#xff09;模式下的AI问答机器人已经逐渐成为企业、个人在办公、生活和学习中的辅助工具。ai问答机器人凭借高效、便捷、智能的特点&#xff0c;为用户提供了全新的交互体验。本文将推荐几款市面上好用的带有ai问答机器人的Sa…

【文心智能体】创建一个属于自己的生活情感类智能体

文章目录 前言一、创建智能体二、体验 前言 智能体技术的快速发展&#xff0c;进一步激发了各行业开发者对其实际应用及用户需求的深入探索。 创建一个属于自己的智能体。文心一言提供了一个很好的平台。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考…

GpuMall智算云:Ubuntu 实例桌面版

基于 ubuntu18.04 安装的桌面版本&#xff0c;桌面使用 xfce4 &#xff0c;集成了 Pytorch2.3.0、cuda11.8、Python3.10、VNC、noVNC、VSCode-Server。 在 镜像市场 选择xfce4-desktop镜像&#xff0c;然后进行创建实例 GpuMall智算云 | 省钱、好用、弹性。租GPU就上GpuMall…

一维前缀和[模版]

题目链接 题目: 分析: 因为要求数组中连续区间的和, 可以使用前缀和算法注意:下标是从1开始算起的, 真正下标0的位置是0第一步: 预处理出来一个前缀和数组dp dp[i] 表示: 表示[1,i] 区间所有元素的和dp[i] dp[i-1] arr[i]例如示例一中: dp数组为{1,3,7}第二步: 使用前缀数…