广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >Spring Security进行登录认证和授权
  • 704
分享到

Spring Security进行登录认证和授权

springjavaspringboot 2023-09-02 07:09:18 704人浏览 八月长安
摘要

一、spring Security内部认证流程 用户首次登录提交用户名和密码后spring security 的UsernamePasswordAuthenticationFilter把用户名密码封

一、spring Security内部认证流程

  1. 用户首次登录提交用户名和密码后spring security 的UsernamePasswordAuthenticationFilter把用户名密码封装Authentication对象

  2. 然后内部调用ProvideManagerauthenticate方法进行认证,然后ProvideManager进一步通过内部调用DaoAuthencationPrioviderauthenticate方法进行认证

  3. DaoAuthencationPriovider通过调用UserDetailsService的实现类InMemoryDetialManagerloadUserByUsername方法查询用户,并把查询到的用户信息封装成一个UserDetails对象。注意InMemoryDetialManage是从内存中查询用户信息的,所以我们要定制化实现UserDetailsService接口来达到从数据库查询用户信息。

  4. 通过PasswordEncode对比返回的UserDetails对象的密码和Authentication对象的密码是否一致,如果密码正确就把UserDetails中的权限信息设置到Authentication对象中

  5. 如果成功返回Authentication对象就使用SecurityContextHolder.getContext().setAuthentication方法存储改对象,然后其他过滤器就会通过SecurityContextHolder来获取当前用户信息。

在这里插入图片描述

二、自定义认证流程

我们要定制化实现UserDetailsService接口来达到从数据库查询用户信息,所以重新写一个UserDetailsServiceImpl实现类。然后认证通过使用用户id生成一个jwt(也就是token),然后用userId 作为key,用户信息作为value存入redis中。用户第一次登陆认证流程如下图。

在这里插入图片描述
导入依赖

  <!--SpringSecurity启动器-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-security</artifactId>        </dependency>        <!--Redis依赖-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-redis</artifactId>        </dependency>        <!--fastJSON依赖-->        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>        </dependency>        <!--Jwt依赖-->        <dependency>            <groupId>io.jsonWEBtoken</groupId>            <artifactId>jjwt</artifactId>        </dependency>

UserDetailsServiceImpl代码

@Servicepublic class UserDetailServiceImpl implements UserDetailsService {    @Resource    private UserMapper userMapper;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        //根据用户名查询用户信息        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();        queryWrapper.eq(User::getUserName,username);        User user = userMapper.selectOne(queryWrapper);        //判断是否查到用户  如果没查到抛出异常        if(Objects.isNull(user)){            throw new RuntimeException("用户名不存在");        }        //返回用户信息        // TODO 查询权限信息封装        return new LoginUser(user);    }}

BlogLoginServiceImpl 登陆认证代码

@Servicepublic class BlogLoginServiceImpl implements BlogLoginService {    @Autowired    private AuthenticationManager authenticationManager;    @Autowired    private RedisCache redisCache;    @Override    public ResponseResult login(User user) {        UsernamePassWordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());        Authentication authenticate = authenticationManager.authenticate(authenticationToken);        //判断是否认证通过       if(Objects.isNull(authenticate)){           throw new RuntimeException("用户名或密码错误");       }        //获取userid 生成token        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();        String userId = loginUser.getUser().getId().toString();        String jwt = JwtUtil.createJWT(userId);        //把用户信息存入redis        redisCache.setCacheObject("bloglogin:"+userId,loginUser);        //把token和userinfo封装 返回        UserInfoVo userInfoVo = BeanCopyUtil.copyBean(loginUser.getUser(), UserInfoVo.class);        BlogUserLoginVo blogUserLoginVo = new BlogUserLoginVo(jwt, userInfoVo);        //把User转换成UserInfoVo        return ResponseResult.okResult(blogUserLoginVo);    }}

SecurityConfig配置类

@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Bean    public PasswordEncoder passwordEncoder(){        return new BCryptPasswordEncoder();    }    @Override    protected void configure(httpsecurity Http) throws Exception {        http                //关闭csrf                .csrf().disable()                //不通过Session获取SecurityContext                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)                .and()                .authorizeRequests()                // 对于登录接口 允许匿名访问                .antMatchers("/login").anonymous()                // 除上面外的所有请求全部不需要认证即可访问                .anyRequest().permitAll();        http.loGout().disable();        //允许跨域        http.cors();    }    @Override    @Bean    public AuthenticationManager authenticationManagerBean() throws Exception {        return super.authenticationManagerBean();    }}

JwtUtil工具

public class JwtUtil {    //有效期为    public static final Long JWT_TTL = 24*60 * 60 *1000L;// 60 * 60 *1000  一个小时    //设置秘钥明文    public static final String JWT_KEY = "hhh";    public static String getUUID(){        String token = UUID.randomUUID().toString().replaceAll("-", "");        return token;    }            public static String createJWT(String subject) {        JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间        return builder.compact();    }        public static String createJWT(String subject, Long ttlMillis) {        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间        return builder.compact();    }    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;        SecreTKEy secretKey = generalKey();        long nowMillis = System.currentTimeMillis();        Date now = new Date(nowMillis);        if(ttlMillis==null){            ttlMillis=JwtUtil.JWT_TTL;        }        long expMillis = nowMillis + ttlMillis;        Date expDate = new Date(expMillis);        return Jwts.builder()                .setId(uuid)              //唯一的ID                .setSubject(subject)   // 主题  可以是JSON数据                .setIssuer("sg")     // 签发者                .setIssuedAt(now)      // 签发时间                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥                .setExpiration(expDate);    }        public static String createJWT(String id, String subject, Long ttlMillis) {        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间        return builder.compact();    }    public static void main(String[] args) throws Exception {        String token = "eyJhbGCiOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";        Claims claims = parseJWT(token);        System.out.println(claims);    }        public static SecretKey generalKey() {        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");        return key;    }            public static Claims parseJWT(String jwt) throws Exception {        SecretKey secretKey = generalKey();        return Jwts.parser()                .setSigningKey(secretKey)                .parseClaimsJws(jwt)                .getBody();    }}

redis缓存配置类

@Configurationpublic class RedisConfig {    @Bean    @SuppressWarnings(value = { "unchecked", "rawtypes" })    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)    {        RedisTemplate<Object, Object> template = new RedisTemplate<>();        template.setConnectionFactory(connectionFactory);        FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);        // 使用StringRedisSerializer来序列化和反序列化redis的key值        template.setKeySerializer(new StringRedisSerializer());        template.setValueSerializer(serializer);        // Hash的key也采用StringRedisSerializer的序列化方式        template.setHashKeySerializer(new StringRedisSerializer());        template.setHashValueSerializer(serializer);        template.afterPropertiesSet();        return template;    }}

下次用户再次发请求时带token(相对于没有前后端分离时的session)来进行访问,如果没有携带则不让访问,如果有就经过一个jwt认证过滤器来获取token,解析token,最后获取userId,封装成Authentication对象存入SecurityContextHolderredis中以便其他过滤器或者资源来获取当前用户请求信息
在这里插入图片描述

最后感悟:SpringBoot约定大于配置,许多接口都可以自定义,达到定制化功能,这也体现了springboot的强大,还有许多功能还没完成,授权功能和流程将在后续完成,敬请期待。

来源地址:https://blog.csdn.net/afjja/article/details/126428728

--结束END--

本文标题: Spring Security进行登录认证和授权

本文链接: https://www.lsjlt.com/news/390179.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作