Spring-Security实现登录接口流程

作者:韭菜盖饭 时间:2023-03-24 05:35:40 

简介

SecuritySpring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。

具体介绍和入门看springSecurity入门

原理初探

在实现之前幺要了解一下登录校验的流程以及SpringSecurity的原理以及认证流程

1、登录校验流程

Spring-Security实现登录接口流程

2、 SpringSecurity完整流程

SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。这里我们可以看看入门案例中的过滤器。

Spring-Security实现登录接口流程

图中只展示了核心过滤器,其它的非核心过滤器并没有在图中展示。

UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请求。入门案例的认证工作主要有它负责。

ExceptionTranslationFilter:处理过滤器链中抛出的任何AccessDeniedExceptionAuthenticationException

FilterSecurityInterceptor:负责权限校验的过滤器。

我们可以通过Debug查看当前系统中SpringSecurity过滤器链中有哪些过滤器及它们的顺序。

Spring-Security实现登录接口流程

3、认证流程

Spring-Security实现登录接口流程

概念速查:

  • Authentication接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。

  • AuthenticationManager接口:定义了认证Authentication的方法

  • UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。

  • UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。

实现思路

登录

①自定义登录接口

调用ProviderManager的方法进行认证 如果认证通过生成jwt

把用户信息存入redis

②自定义UserDetailsService

在这个实现类中去查询数据库

校验:

①定义Jwt认证过滤器

获取token

解析token获取其中的userid

redis中获取用户信息

存入SecurityContextHolder

这里我们主要是实现登录接口的功能
核心思想就是就是将认证流程 中的UserDetailsService 重写其loadUserByUsername的方法,使其从数据库中查询数据,然后用自己的类(我这里是UserLogin)实现UserDetails,在loadUserByUsername中返回UserLogin

登录接口实现

准备工作

添加依赖,添加工具类,创建数据库,编写配置文件
1、添加依赖

<!--redis依赖-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-redis</artifactId>
       </dependency>
       <!--fastjson依赖-->
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.33</version>
       </dependency>
       <!--jwt依赖-->
       <dependency>
           <groupId>io.jsonwebtoken</groupId>
           <artifactId>jjwt</artifactId>
           <version>0.9.0</version>
       </dependency>
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>3.4.3</version>
       </dependency>
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
       </dependency>

2、添加Redis相关配置

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
/**
* Redis使用FastJson序列化
*
* @author sg
*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>
{
   public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
   private Class<T> clazz;
   static
   {
       ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
   }
   public FastJsonRedisSerializer(Class<T> clazz)
   {
       super();
       this.clazz = clazz;
   }
   @Override
   public byte[] serialize(T t) throws SerializationException
   {
       if (t == null)
       {
           return new byte[0];
       }
       return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
   }
   @Override
   public T deserialize(byte[] bytes) throws SerializationException
   {
       if (bytes == null || bytes.length <= 0)
       {
           return null;
       }
       String str = new String(bytes, DEFAULT_CHARSET);
       return JSON.parseObject(str, clazz);
   }
   protected JavaType getJavaType(Class<?> clazz)
   {
       return TypeFactory.defaultInstance().constructType(clazz);
   }
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public 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;
   }
}

封装返回的数据

import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> {
   /**
    * 状态码
    */
   private Integer code;
   /**
    * 提示信息,如果有错误时,前端可以获取该字段进行提示
    */
   private String msg;
   /**
    * 查询到的结果数据,
    */
   private T data;
   public ResponseResult(Integer code, String msg) {
       this.code = code;
       this.msg = msg;
   }
   public ResponseResult(Integer code, T data) {
       this.code = code;
       this.data = data;
   }
   public Integer getCode() {
       return code;
   }
   public void setCode(Integer code) {
       this.code = code;
   }
   public String getMsg() {
       return msg;
   }
   public void setMsg(String msg) {
       this.msg = msg;
   }
   public T getData() {
       return data;
   }
   public void setData(T data) {
       this.data = data;
   }
   public ResponseResult(Integer code, String msg, T data) {
       this.code = code;
       this.msg = msg;
       this.data = data;
   }
}

JWT工具类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
/**
* JWT工具类
*/
public class JwtUtil {
   //有效期为
   public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一个小时
   //设置秘钥明文
   public static final String JWT_KEY = "sangeng";
   public static String getUUID(){
       String token = UUID.randomUUID().toString().replaceAll("-", "");
       return token;
   }
   /**
    * 生成jtw
    * @param subject token中要存放的数据(json格式)
    * @return
    */
   public static String createJWT(String subject) {
       JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
       return builder.compact();
   }
   /**
    * 生成jtw
    * @param subject token中要存放的数据(json格式)
    * @param ttlMillis token超时时间
    * @return
    */
   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);
   }
   /**
    * 创建token
    * @param id
    * @param subject
    * @param ttlMillis
    * @return
    */
   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);
   }
   /**
    * 生成加密后的秘钥 secretKey
    * @return
    */
   public static SecretKey generalKey() {
       byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
       SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
       return key;
   }
   /**
    * 解析
    *
    * @param jwt
    * @return
    * @throws Exception
    */
   public static Claims parseJWT(String jwt) throws Exception {
       SecretKey secretKey = generalKey();
       return Jwts.parser()
               .setSigningKey(secretKey)
               .parseClaimsJws(jwt)
               .getBody();
   }
}

Redis工具类

import java.util.*;
import java.util.concurrent.TimeUnit;
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
   @Autowired
   public RedisTemplate redisTemplate;
   /**
    * 缓存基本的对象,Integer、String、实体类等
    *
    * @param key 缓存的键值
    * @param value 缓存的值
    */
   public <T> void setCacheObject(final String key, final T value)
   {
       redisTemplate.opsForValue().set(key, value);
   }
   /**
    * 缓存基本的对象,Integer、String、实体类等
    *
    * @param key 缓存的键值
    * @param value 缓存的值
    * @param timeout 时间
    * @param timeUnit 时间颗粒度
    */
   public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
   {
       redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
   }
   /**
    * 设置有效时间
    *
    * @param key Redis键
    * @param timeout 超时时间
    * @return true=设置成功;false=设置失败
    */
   public boolean expire(final String key, final long timeout)
   {
       return expire(key, timeout, TimeUnit.SECONDS);
   }
   /**
    * 设置有效时间
    *
    * @param key Redis键
    * @param timeout 超时时间
    * @param unit 时间单位
    * @return true=设置成功;false=设置失败
    */
   public boolean expire(final String key, final long timeout, final TimeUnit unit)
   {
       return redisTemplate.expire(key, timeout, unit);
   }
   /**
    * 获得缓存的基本对象。
    *
    * @param key 缓存键值
    * @return 缓存键值对应的数据
    */
   public <T> T getCacheObject(final String key)
   {
       ValueOperations<String, T> operation = redisTemplate.opsForValue();
       return operation.get(key);
   }
   /**
    * 删除单个对象
    *
    * @param key
    */
   public boolean deleteObject(final String key)
   {
       return redisTemplate.delete(key);
   }
   /**
    * 删除集合对象
    *
    * @param collection 多个对象
    * @return
    */
   public long deleteObject(final Collection collection)
   {
       return redisTemplate.delete(collection);
   }
   /**
    * 缓存List数据
    *
    * @param key 缓存的键值
    * @param dataList 待缓存的List数据
    * @return 缓存的对象
    */
   public <T> long setCacheList(final String key, final List<T> dataList)
   {
       Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
       return count == null ? 0 : count;
   }
   /**
    * 获得缓存的list对象
    *
    * @param key 缓存的键值
    * @return 缓存键值对应的数据
    */
   public <T> List<T> getCacheList(final String key)
   {
       return redisTemplate.opsForList().range(key, 0, -1);
   }
   /**
    * 缓存Set
    *
    * @param key 缓存键值
    * @param dataSet 缓存的数据
    * @return 缓存数据的对象
    */
   public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
   {
       BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
       Iterator<T> it = dataSet.iterator();
       while (it.hasNext())
       {
           setOperation.add(it.next());
       }
       return setOperation;
   }
   /**
    * 获得缓存的set
    *
    * @param key
    * @return
    */
   public <T> Set<T> getCacheSet(final String key)
   {
       return redisTemplate.opsForSet().members(key);
   }
   /**
    * 缓存Map
    *
    * @param key
    * @param dataMap
    */
   public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
   {
       if (dataMap != null) {
           redisTemplate.opsForHash().putAll(key, dataMap);
       }
   }
   /**
    * 获得缓存的Map
    *
    * @param key
    * @return
    */
   public <T> Map<String, T> getCacheMap(final String key)
   {
       return redisTemplate.opsForHash().entries(key);
   }
   /**
    * 往Hash中存入数据
    *
    * @param key Redis键
    * @param hKey Hash键
    * @param value 值
    */
   public <T> void setCacheMapValue(final String key, final String hKey, final T value)
   {
       redisTemplate.opsForHash().put(key, hKey, value);
   }
   /**
    * 获取Hash中的数据
    *
    * @param key Redis键
    * @param hKey Hash键
    * @return Hash中的对象
    */
   public <T> T getCacheMapValue(final String key, final String hKey)
   {
       HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
       return opsForHash.get(key, hKey);
   }
   /**
    * 删除Hash中的数据
    *
    * @param key
    * @param hkey
    */
   public void delCacheMapValue(final String key, final String hkey)
   {
       HashOperations hashOperations = redisTemplate.opsForHash();
       hashOperations.delete(key, hkey);
   }
   /**
    * 获取多个Hash中的数据
    *
    * @param key Redis键
    * @param hKeys Hash键集合
    * @return Hash对象集合
    */
   public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
   {
       return redisTemplate.opsForHash().multiGet(key, hKeys);
   }
   /**
    * 获得缓存的基本对象列表
    *
    * @param pattern 字符串前缀
    * @return 对象列表
    */
   public Collection<String> keys(final String pattern)
   {
       return redisTemplate.keys(pattern);
   }
}

将字符串渲染到客户端工具类

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class WebUtils
{
   /**
    * 将字符串渲染到客户端
    *
    * @param response 渲染对象
    * @param string 待渲染的字符串
    * @return null
    */
   public static String renderString(HttpServletResponse response, String string) {
       try
       {
           response.setStatus(200);
           response.setContentType("application/json");
           response.setCharacterEncoding("utf-8");
           response.getWriter().print(string);
       }
       catch (IOException e)
       {
           e.printStackTrace();
       }
       return null;
   }
}

SQL语句创建用户表

CREATE TABLE `sys_user` (
 `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
 `user_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '用户名',
 `nick_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '昵称',
 `password` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '密码',
 `status` CHAR(1) DEFAULT '0' COMMENT '账号状态(0正常 1停用)',
 `email` VARCHAR(64) DEFAULT NULL COMMENT '邮箱',
 `phonenumber` VARCHAR(32) DEFAULT NULL COMMENT '手机号',
 `sex` CHAR(1) DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',
 `avatar` VARCHAR(128) DEFAULT NULL COMMENT '头像',
 `user_type` CHAR(1) NOT NULL DEFAULT '1' COMMENT '用户类型(0管理员,1普通用户)',
 `create_by` BIGINT(20) DEFAULT NULL COMMENT '创建人的用户id',
 `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
 `update_by` BIGINT(20) DEFAULT NULL COMMENT '更新人',
 `update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
 `del_flag` INT(11) DEFAULT '0' COMMENT '删除标志(0代表未删除,1代表已删除)',
 PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户表'

配置文件

spring:
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/你的数据库名称?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
   username: ****
   password: ****
 redis:
   # Redis服务器地址
   host:  127.0.0.1
   # Redis服务器连接端口
   port: 6379
   # Redis服务器连接密码
   password:
   # Redis数据库索引
   database: 3
   # 连接超时时间(毫秒)
   timeout: 30000
   lettuce:
     pool:
       max-active: 50
       max-wait: -1
       max-idle: 50
       min-idle: 1

编码实现

目录结构
关于Email的类不用管

Spring-Security实现登录接口流程

配置类
RedisConfig类

package com.zzuli.common.config;
import com.zzuli.common.utils.FastJsonRedisSerializer;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
public class RedisConfig {
   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;
   }
}

SecurityConfig

package com.zzuli.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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;
/**
* @author niuben
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   //创建BCryptPasswordEncoder注入容器
   @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("/user/login").anonymous()
               // 除上面外的所有请求全部需要鉴权认证
               .anyRequest().authenticated();
   }
   @Bean
   @Override
   public AuthenticationManager authenticationManagerBean() throws Exception {
       return super.authenticationManagerBean();
   }
}

实体类
User类

package com.zzuli.pojo;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 用户表(User)实体类
*
* @author 三更
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("sys_user")   //数据库的用户名名称
public class User implements Serializable {
   private static final long serialVersionUID = -40356785423868312L;
   /**
   * 主键
   */
   @TableId
   private Long id;
   /**
   * 用户名
   */
   private String userName;
   /**
   * 昵称
   */
   private String nickName;
   /**
   * 密码
   */
   private String password;
   /**
   * 账号状态(0正常 1停用)
   */
   private String status;
   /**
   * 邮箱
   */
   private String email;
   /**
   * 手机号
   */
   private String phonenumber;
   /**
   * 用户性别(0男,1女,2未知)
   */
   private String sex;
   /**
   * 头像
   */
   private String avatar;
   /**
   * 用户类型(0管理员,1普通用户)
   */
   private String userType;
   /**
   * 创建人的用户id
   */
   private Long createBy;
   /**
   * 创建时间
   */
   private Date createTime;
   /**
   * 更新人
   */
   private Long updateBy;
   /**
   * 更新时间
   */
   private Date updateTime;
   /**
   * 删除标志(0代表未删除,1代表已删除)
   */
   private Integer delFlag;
}

UserLogin
继承UserDetails

package com.zzuli.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* @author niuben
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserLogin implements UserDetails {
   private User user;
   @Override
   public Collection<? extends GrantedAuthority> getAuthorities() {
       return null;
   }
   @Override
   public String getPassword() {
       return user.getPassword();
   }
   @Override
   public String getUsername() {
       return user.getUserName();
   }
   @Override
   public boolean isAccountNonExpired() {
       return true;
   }
   @Override
   public boolean isAccountNonLocked() {
       return true;
   }
   @Override
   public boolean isCredentialsNonExpired() {
       return true;
   }
   @Override
   public boolean isEnabled() {
       return true;
   }
}

接口实现类
LoginController接口

package com.zzuli.controller;
import com.zzuli.common.api.CommonResult;
import com.zzuli.pojo.User;
import com.zzuli.service.LoginService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author niuben
*/
@RestController
public class LoginController {
   @Resource
   private LoginService loginService;
   @PostMapping("/user/login")
   public CommonResult<Object> login(@RequestBody User user){
       CommonResult<Object> result = null;
       try {
           result = loginService.login(user);
       } catch (Exception e) {
          return CommonResult.success("账户或者密码错误!");
       }
       return result;
   }
}

UserDetailsServiceImpl实现类

package com.zzuli.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzuli.mapper.UserMapper;
import com.zzuli.pojo.User;
import com.zzuli.pojo.UserLogin;
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 javax.annotation.Resource;
import java.util.Objects;
/**
* @author niuben
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
   @Resource
   private UserMapper userMapper;
   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
       //查询用户信息
       LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>();
       queryWrapper.eq(User::getUserName,username);
       User user = userMapper.selectOne(queryWrapper);
       //如果没有查询到用户,就抛出异常
       if(Objects.isNull(user)){
           throw new RuntimeException("用户名或者密码错误!");
       }
       //将数据封装成UserDetails
       return new UserLogin(user);
   }
}

LoginService接口

package com.zzuli.service;
import com.zzuli.common.api.CommonResult;
import com.zzuli.pojo.User;
/**
* @author niuben
*/
public interface LoginService {
   CommonResult<Object> login(User user);
}

LoginService实现接口

package com.zzuli.service.impl;
import com.zzuli.common.api.CommonResult;
import com.zzuli.common.utils.JwtUtil;
import com.zzuli.common.utils.RedisCache;
import com.zzuli.pojo.User;
import com.zzuli.pojo.UserLogin;
import com.zzuli.service.LoginService;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author niuben
*/
@Service
public class LoginServiceImpl implements LoginService {
   @Resource
   private AuthenticationManager authenticationManager;
   @Resource
   private RedisCache redisCache;
   @Override
   public CommonResult<Object> login(User user) {
       //进行用户认证
       UsernamePasswordAuthenticationToken authenticationToken =
               new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
       Authentication authenticate = authenticationManager.authenticate(authenticationToken);
       //认证未通过,给出提示
       if(Objects.isNull(authenticate)){
           throw new RuntimeException("登陆失败!");
       }
       //通过了,生成jwt
       UserLogin loginUser = (UserLogin) authenticate.getPrincipal();
       String id = loginUser.getUser().getId().toString();
       String jwt = JwtUtil.createJWT(id);
       Map<String,String> map = new HashMap<>();
       map.put("token",jwt);
       //将用户信息存入redis
       redisCache.setCacheObject("login"+id,loginUser);
       return CommonResult.success(map,"登陆成功!");
   }
}

UserMapper

package com.zzuli.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zzuli.pojo.User;
public interface UserMapper extends BaseMapper<User> {
}

启动类

package com.zzuli;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.zzuli.mapper")  //mapper扫描器
public class AppleunlockManageApplication {
   public static void main(String[] args) {
       SpringApplication.run(AppleunlockManageApplication.class, args);
   }
}

效果展示

注意:测试时要打开redis,数据库中的密码必须是加密后的
可以使用一下方法进行加密,passwordEncoderSecurityConfig中已经注入的

@Resource
   private PasswordEncoder passwordEncoder;
String encode = passwordEncoder.encode("123456");
   System.out.println(encode);

Spring-Security实现登录接口流程

登录接口测试

Spring-Security实现登录接口流程

来源:https://blog.csdn.net/niulinbiao/article/details/128881220

标签:Spring-Security,登录,接口
0
投稿

猜你喜欢

  • C++利用ImGUI绘制D3D外部菜单

    2023-11-18 12:35:00
  • C#实现简易计算器功能(1)(窗体应用)

    2022-09-30 16:59:16
  • Java如何跳过https的ssl证书验证详解

    2023-08-24 11:34:56
  • eclipse导入appcompat项目报错解决办法

    2021-11-10 14:25:57
  • 浅谈android @id和@+id的区别

    2021-10-28 06:06:09
  • XAML如何获取元素的位置

    2023-03-16 14:24:12
  • WinForm实现状态栏跑马灯效果的方法示例

    2023-07-16 19:37:33
  • IntelliJ IDEA Project窗口的一些设置详解

    2023-11-09 04:54:44
  • Java压缩文件ZIP实例代码

    2022-03-25 04:08:27
  • Android 自定义来电秀实现总结

    2023-12-17 07:52:32
  • Java流式操作之Collectors工具类操作指南

    2022-12-01 01:25:55
  • 关于Lambda表达式的方法引用和构造器引用简的单示例

    2022-09-18 05:02:08
  • Gradle进阶使用结合Sonarqube进行代码审查的方法

    2021-12-17 08:49:56
  • Android实现仪表盘效果

    2021-07-26 13:19:37
  • Android EditText自定义样式的方法

    2021-10-06 22:37:37
  • C#窗体间通讯的几种常用处理方法总结

    2021-10-26 05:48:49
  • SpringCloud中分析讲解Feign组件添加请求头有哪些坑梳理

    2023-05-01 19:30:11
  • java 画pdf用itext调整表格宽度、自定义各个列宽的方法

    2021-07-12 04:16:10
  • Spring Boot conditional注解用法详解

    2022-03-19 02:32:25
  • Android消息机制Handler的工作过程详解

    2023-07-31 13:49:03
  • asp之家 软件编程 m.aspxhome.com