Spring-Security實現(xiàn)登錄接口流程
簡介
Security 是 Spring 家族中的一個安全管理框架。相比與另外一個安全框架Shiro,它提供了更豐富的功能,社區(qū)資源也比Shiro豐富。
具體介紹和入門看springSecurity入門
原理初探
在實現(xiàn)之前幺要了解一下登錄校驗的流程以及SpringSecurity的原理以及認證流程
1、登錄校驗流程

2、 SpringSecurity完整流程
SpringSecurity的原理其實就是一個過濾器鏈,內(nèi)部包含了提供各種功能的過濾器。這里我們可以看看入門案例中的過濾器。

圖中只展示了核心過濾器,其它的非核心過濾器并沒有在圖中展示。
UsernamePasswordAuthenticationFilter:負責(zé)處理我們在登陸頁面填寫了用戶名密碼后的登陸請求。入門案例的認證工作主要有它負責(zé)。
ExceptionTranslationFilter:處理過濾器鏈中拋出的任何AccessDeniedException和AuthenticationException 。
FilterSecurityInterceptor:負責(zé)權(quán)限校驗的過濾器。
我們可以通過Debug查看當(dāng)前系統(tǒng)中SpringSecurity過濾器鏈中有哪些過濾器及它們的順序。

3、認證流程

概念速查:
Authentication接口: 它的實現(xiàn)類,表示當(dāng)前訪問系統(tǒng)的用戶,封裝了用戶相關(guān)信息。AuthenticationManager接口:定義了認證Authentication的方法UserDetailsService接口:加載用戶特定數(shù)據(jù)的核心接口。里面定義了一個根據(jù)用戶名查詢用戶信息的方法。UserDetails接口:提供核心用戶信息。通過UserDetailsService根據(jù)用戶名獲取處理的用戶信息要封裝成UserDetails對象返回。然后將這些信息封裝到Authentication對象中。
實現(xiàn)思路
登錄
? ①自定義登錄接口
? 調(diào)用ProviderManager的方法進行認證 如果認證通過生成jwt
? 把用戶信息存入redis中
? ②自定義UserDetailsService
? 在這個實現(xiàn)類中去查詢數(shù)據(jù)庫
校驗:
? ①定義Jwt認證過濾器
? 獲取token
? 解析token獲取其中的userid
? 從redis中獲取用戶信息
? 存入SecurityContextHolder
這里我們主要是實現(xiàn)登錄接口的功能
核心思想就是就是將認證流程 中的UserDetailsService 重寫其loadUserByUsername的方法,使其從數(shù)據(jù)庫中查詢數(shù)據(jù),然后用自己的類(我這里是UserLogin)實現(xiàn)UserDetails,在loadUserByUsername中返回UserLogin
登錄接口實現(xiàn)
準備工作
添加依賴,添加工具類,創(chuàng)建數(shù)據(jù)庫,編寫配置文件
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相關(guān)配置
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;
}
}封裝返回的數(shù)據(jù)
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> {
/**
* 狀態(tài)碼
*/
private Integer code;
/**
* 提示信息,如果有錯誤時,前端可以獲取該字段進行提示
*/
private String msg;
/**
* 查詢到的結(jié)果數(shù)據(jù),
*/
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 一個小時
//設(shè)置秘鑰明文
public static final String JWT_KEY = "sangeng";
public static String getUUID(){
String token = UUID.randomUUID().toString().replaceAll("-", "");
return token;
}
/**
* 生成jtw
* @param subject token中要存放的數(shù)據(jù)(json格式)
* @return
*/
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 設(shè)置過期時間
return builder.compact();
}
/**
* 生成jtw
* @param subject token中要存放的數(shù)據(jù)(json格式)
* @param ttlMillis token超時時間
* @return
*/
public static String createJWT(String subject, Long ttlMillis) {
JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 設(shè)置過期時間
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數(shù)據(jù)
.setIssuer("sg") // 簽發(fā)者
.setIssuedAt(now) // 簽發(fā)時間
.signWith(signatureAlgorithm, secretKey) //使用HS256對稱加密算法簽名, 第二個參數(shù)為秘鑰
.setExpiration(expDate);
}
/**
* 創(chuàng)建token
* @param id
* @param subject
* @param ttlMillis
* @return
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 設(shè)置過期時間
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);
}
/**
* 設(shè)置有效時間
*
* @param key Redis鍵
* @param timeout 超時時間
* @return true=設(shè)置成功;false=設(shè)置失敗
*/
public boolean expire(final String key, final long timeout)
{
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 設(shè)置有效時間
*
* @param key Redis鍵
* @param timeout 超時時間
* @param unit 時間單位
* @return true=設(shè)置成功;false=設(shè)置失敗
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit)
{
return redisTemplate.expire(key, timeout, unit);
}
/**
* 獲得緩存的基本對象。
*
* @param key 緩存鍵值
* @return 緩存鍵值對應(yīng)的數(shù)據(jù)
*/
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數(shù)據(jù)
*
* @param key 緩存的鍵值
* @param dataList 待緩存的List數(shù)據(jù)
* @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 緩存鍵值對應(yīng)的數(shù)據(jù)
*/
public <T> List<T> getCacheList(final String key)
{
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 緩存Set
*
* @param key 緩存鍵值
* @param dataSet 緩存的數(shù)據(jù)
* @return 緩存數(shù)據(jù)的對象
*/
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中存入數(shù)據(jù)
*
* @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中的數(shù)據(jù)
*
* @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中的數(shù)據(jù)
*
* @param key
* @param hkey
*/
public void delCacheMapValue(final String key, final String hkey)
{
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hkey);
}
/**
* 獲取多個Hash中的數(shù)據(jù)
*
* @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語句創(chuàng)建用戶表
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 '賬號狀態(tài)(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 '創(chuàng)建人的用戶id', `create_time` DATETIME DEFAULT NULL COMMENT '創(chuàng)建時間', `update_by` BIGINT(20) DEFAULT NULL COMMENT '更新人', `update_time` DATETIME DEFAULT NULL COMMENT '更新時間', `del_flag` INT(11) DEFAULT '0' COMMENT '刪除標(biāo)志(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/你的數(shù)據(jù)庫名稱?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
username: ****
password: ****
redis:
# Redis服務(wù)器地址
host: 127.0.0.1
# Redis服務(wù)器連接端口
port: 6379
# Redis服務(wù)器連接密碼
password:
# Redis數(shù)據(jù)庫索引
database: 3
# 連接超時時間(毫秒)
timeout: 30000
lettuce:
pool:
max-active: 50
max-wait: -1
max-idle: 50
min-idle: 1編碼實現(xiàn)
目錄結(jié)構(gòu)
關(guān)于Email的類不用管

配置類
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 {
//創(chuàng)建BCryptPasswordEncoder注入容器
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//關(guān)閉csrf
.csrf().disable()
//不通過Session獲取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 對于登錄接口 允許匿名訪問
.antMatchers("/user/login").anonymous()
// 除上面外的所有請求全部需要鑒權(quán)認證
.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") //數(shù)據(jù)庫的用戶名名稱
public class User implements Serializable {
private static final long serialVersionUID = -40356785423868312L;
/**
* 主鍵
*/
@TableId
private Long id;
/**
* 用戶名
*/
private String userName;
/**
* 昵稱
*/
private String nickName;
/**
* 密碼
*/
private String password;
/**
* 賬號狀態(tài)(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;
/**
* 創(chuàng)建人的用戶id
*/
private Long createBy;
/**
* 創(chuàng)建時間
*/
private Date createTime;
/**
* 更新人
*/
private Long updateBy;
/**
* 更新時間
*/
private Date updateTime;
/**
* 刪除標(biāo)志(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;
}
}接口實現(xiàn)類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實現(xiàn)類
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("用戶名或者密碼錯誤!");
}
//將數(shù)據(jù)封裝成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實現(xiàn)接口
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,數(shù)據(jù)庫中的密碼必須是加密后的
可以使用一下方法進行加密,passwordEncoder是SecurityConfig中已經(jīng)注入的
@Resource
private PasswordEncoder passwordEncoder;
String encode = passwordEncoder.encode("123456");
System.out.println(encode);
登錄接口測試

到此這篇關(guān)于Spring-Security實現(xiàn)登錄接口流程的文章就介紹到這了,更多相關(guān)Spring-Security登錄接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Spring?Cloud?Stream處理Java消息流的操作流程
Spring?Cloud?Stream是一個用于構(gòu)建消息驅(qū)動微服務(wù)的框架,能夠與各種消息中間件集成,如RabbitMQ、Kafka等,今天我們來探討如何使用Spring?Cloud?Stream來處理Java消息流,需要的朋友可以參考下2024-08-08
Spring Security中的Servlet過濾器體系代碼分析
這篇文章主要介紹了Spring Security中的Servlet過濾器體系,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
idea下如何設(shè)置項目啟動的JVM運行內(nèi)存大小
這篇文章主要介紹了idea下如何設(shè)置項目啟動的JVM運行內(nèi)存大小問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
Spring?cloud?Hystrix注解初始化源碼過程解讀
這篇文章主要為大家介紹了Hystrix初始化部分,我們從源碼的角度分析一下@EnableCircuitBreaker以及@HystrixCommand注解的初始化過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-12-12
Java零基礎(chǔ)教程之Windows下安裝 JDK的方法圖解
這篇文章主要介紹了Java零基礎(chǔ)教程之Windows下安裝 JDK的方法圖解,本文介紹的非常詳細,具有參考借鑒價值,需要的朋友可以參考下2016-09-09
IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實例解析
這篇文章主要介紹了IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實例解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07

