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,一经查实,立即删除!

相关文章

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项目…

微星打造全球首款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.…

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

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

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}第二步: 使用前缀数…

RabbitMQ - SimpleMessageListenerContainer的实现逻辑

RabbitMQ - SimpleMessageListenerContainer的实现逻辑 Queue&#xff08;队列&#xff09;&#xff1a;在 RabbitMQ 中用于存储消息的数据结构。生产者将消息发送到队列中&#xff0c;而消费者从队列中接收消息。 Connection&#xff08;连接&#xff09;&#xff1a;连接是应…

Python代码实现代价函数

最小二乘法 最小二乘法是一种在统计学、数学、工程学和计算机科学等领域广泛使用的优化方法。 基本原理 最小二乘法的主要目的是找到一组模型参数&#xff0c;使得根据这些参数所预测的数据与实际观测数据之间的差异&#xff08;即残差&#xff09;的平方和最小。 数学表达…

5.22R语言初步学习-1

今天上课讲R语言&#xff0c;要干什么没讲&#xff0c;分析什么&#xff0c;目的是什么没讲。助教基本上就是让我们打开窗口&#xff0c;按要求抄代码指令&#xff0c;代码原理也没讲......再加上最近正好在学概率论与数理统计&#xff0c;肯定是有用的&#xff0c;所以还是学习…

PHP质量工具系列之php_CodeSniffer

PHP_CodeSniffer 是一组两个 PHP 脚本&#xff1a;主脚本 phpcs 对 PHP、JavaScript 和 CSS 文件进行标记&#xff0c;以检测是否违反定义的编码标准&#xff1b;第二个脚本 phpcbf 自动纠正违反编码标准的行为。PHP_CodeSniffer 是一个重要的开发工具&#xff0c;可以确保你的…