Spring-Security實(shí)現(xiàn)登錄接口流程
簡(jiǎn)介
Security
是 Spring
家族中的一個(gè)安全管理框架。相比與另外一個(gè)安全框架Shiro
,它提供了更豐富的功能,社區(qū)資源也比Shiro豐富。
具體介紹和入門看springSecurity入門
原理初探
在實(shí)現(xiàn)之前幺要了解一下登錄校驗(yàn)的流程以及SpringSecurity
的原理以及認(rèn)證流程
1、登錄校驗(yàn)流程
2、 SpringSecurity完整流程
SpringSecurity
的原理其實(shí)就是一個(gè)過(guò)濾器鏈,內(nèi)部包含了提供各種功能的過(guò)濾器。這里我們可以看看入門案例中的過(guò)濾器。
圖中只展示了核心過(guò)濾器,其它的非核心過(guò)濾器并沒(méi)有在圖中展示。
UsernamePasswordAuthenticationFilter:負(fù)責(zé)處理我們?cè)诘顷戫?yè)面填寫(xiě)了用戶名密碼后的登陸請(qǐng)求。入門案例的認(rèn)證工作主要有它負(fù)責(zé)。
ExceptionTranslationFilter:處理過(guò)濾器鏈中拋出的任何AccessDeniedException
和AuthenticationException
。
FilterSecurityInterceptor:負(fù)責(zé)權(quán)限校驗(yàn)的過(guò)濾器。
我們可以通過(guò)Debug
查看當(dāng)前系統(tǒng)中SpringSecurity
過(guò)濾器鏈中有哪些過(guò)濾器及它們的順序。
3、認(rèn)證流程
概念速查:
Authentication
接口: 它的實(shí)現(xiàn)類,表示當(dāng)前訪問(wèn)系統(tǒng)的用戶,封裝了用戶相關(guān)信息。AuthenticationManager
接口:定義了認(rèn)證Authentication的方法UserDetailsService
接口:加載用戶特定數(shù)據(jù)的核心接口。里面定義了一個(gè)根據(jù)用戶名查詢用戶信息的方法。UserDetails
接口:提供核心用戶信息。通過(guò)UserDetailsService
根據(jù)用戶名獲取處理的用戶信息要封裝成UserDetails
對(duì)象返回。然后將這些信息封裝到Authentication
對(duì)象中。
實(shí)現(xiàn)思路
登錄
? ①自定義登錄接口
? 調(diào)用ProviderManager
的方法進(jìn)行認(rèn)證 如果認(rèn)證通過(guò)生成jwt
? 把用戶信息存入redis
中
? ②自定義UserDetailsService
? 在這個(gè)實(shí)現(xiàn)類中去查詢數(shù)據(jù)庫(kù)
校驗(yàn):
? ①定義Jwt認(rèn)證過(guò)濾器
? 獲取token
? 解析token
獲取其中的userid
? 從redis
中獲取用戶信息
? 存入SecurityContextHolder
這里我們主要是實(shí)現(xiàn)登錄接口的功能
核心思想就是就是將認(rèn)證流程
中的UserDetailsService
重寫(xiě)其loadUserByUsername
的方法,使其從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù),然后用自己的類(我這里是UserLogin
)實(shí)現(xiàn)UserDetails
,在loadUserByUsername
中返回UserLogin
登錄接口實(shí)現(xiàn)
準(zhǔn)備工作
添加依賴,添加工具類,創(chuàng)建數(shù)據(jù)庫(kù),編寫(xiě)配置文件
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來(lái)序列化和反序列化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; /** * 提示信息,如果有錯(cuò)誤時(shí),前端可以獲取該字段進(jìn)行提示 */ 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 一個(gè)小時(shí) //設(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è)置過(guò)期時(shí)間 return builder.compact(); } /** * 生成jtw * @param subject token中要存放的數(shù)據(jù)(json格式) * @param ttlMillis token超時(shí)時(shí)間 * @return */ public static String createJWT(String subject, Long ttlMillis) { JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 設(shè)置過(guò)期時(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ā)時(shí)間 .signWith(signatureAlgorithm, secretKey) //使用HS256對(duì)稱加密算法簽名, 第二個(gè)參數(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è)置過(guò)期時(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; /** * 緩存基本的對(duì)象,Integer、String、實(shí)體類等 * * @param key 緩存的鍵值 * @param value 緩存的值 */ public <T> void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value); } /** * 緩存基本的對(duì)象,Integer、String、實(shí)體類等 * * @param key 緩存的鍵值 * @param value 緩存的值 * @param timeout 時(shí)間 * @param timeUnit 時(shí)間顆粒度 */ public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { redisTemplate.opsForValue().set(key, value, timeout, timeUnit); } /** * 設(shè)置有效時(shí)間 * * @param key Redis鍵 * @param timeout 超時(shí)時(shí)間 * @return true=設(shè)置成功;false=設(shè)置失敗 */ public boolean expire(final String key, final long timeout) { return expire(key, timeout, TimeUnit.SECONDS); } /** * 設(shè)置有效時(shí)間 * * @param key Redis鍵 * @param timeout 超時(shí)時(shí)間 * @param unit 時(shí)間單位 * @return true=設(shè)置成功;false=設(shè)置失敗 */ public boolean expire(final String key, final long timeout, final TimeUnit unit) { return redisTemplate.expire(key, timeout, unit); } /** * 獲得緩存的基本對(duì)象。 * * @param key 緩存鍵值 * @return 緩存鍵值對(duì)應(yīng)的數(shù)據(jù) */ public <T> T getCacheObject(final String key) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); return operation.get(key); } /** * 刪除單個(gè)對(duì)象 * * @param key */ public boolean deleteObject(final String key) { return redisTemplate.delete(key); } /** * 刪除集合對(duì)象 * * @param collection 多個(gè)對(duì)象 * @return */ public long deleteObject(final Collection collection) { return redisTemplate.delete(collection); } /** * 緩存List數(shù)據(jù) * * @param key 緩存的鍵值 * @param dataList 待緩存的List數(shù)據(jù) * @return 緩存的對(duì)象 */ public <T> long setCacheList(final String key, final List<T> dataList) { Long count = redisTemplate.opsForList().rightPushAll(key, dataList); return count == null ? 0 : count; } /** * 獲得緩存的list對(duì)象 * * @param key 緩存的鍵值 * @return 緩存鍵值對(duì)應(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ù)的對(duì)象 */ 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中的對(duì)象 */ 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); } /** * 獲取多個(gè)Hash中的數(shù)據(jù) * * @param key Redis鍵 * @param hKeys Hash鍵集合 * @return Hash對(duì)象集合 */ public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) { return redisTemplate.opsForHash().multiGet(key, hKeys); } /** * 獲得緩存的基本對(duì)象列表 * * @param pattern 字符串前綴 * @return 對(duì)象列表 */ 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 渲染對(duì)象 * @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語(yǔ)句創(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 '賬號(hào)狀態(tài)(0正常 1停用)', `email` VARCHAR(64) DEFAULT NULL COMMENT '郵箱', `phonenumber` VARCHAR(32) DEFAULT NULL COMMENT '手機(jī)號(hào)', `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)建時(shí)間', `update_by` BIGINT(20) DEFAULT NULL COMMENT '更新人', `update_time` DATETIME DEFAULT NULL COMMENT '更新時(shí)間', `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ù)庫(kù)名稱?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ù)庫(kù)索引 database: 3 # 連接超時(shí)時(shí)間(毫秒) timeout: 30000 lettuce: pool: max-active: 50 max-wait: -1 max-idle: 50 min-idle: 1
編碼實(shí)現(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來(lái)序列化和反序列化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() //不通過(guò)Session獲取SecurityContext .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() // 對(duì)于登錄接口 允許匿名訪問(wèn) .antMatchers("/user/login").anonymous() // 除上面外的所有請(qǐng)求全部需要鑒權(quán)認(rèn)證 .anyRequest().authenticated(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
實(shí)體類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)實(shí)體類 * * @author 三更 */ @Data @AllArgsConstructor @NoArgsConstructor @TableName("sys_user") //數(shù)據(jù)庫(kù)的用戶名名稱 public class User implements Serializable { private static final long serialVersionUID = -40356785423868312L; /** * 主鍵 */ @TableId private Long id; /** * 用戶名 */ private String userName; /** * 昵稱 */ private String nickName; /** * 密碼 */ private String password; /** * 賬號(hào)狀態(tài)(0正常 1停用) */ private String status; /** * 郵箱 */ private String email; /** * 手機(jī)號(hào) */ 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)建時(shí)間 */ private Date createTime; /** * 更新人 */ private Long updateBy; /** * 更新時(shí)間 */ 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; } }
接口實(shí)現(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("賬戶或者密碼錯(cuò)誤!"); } return result; } }
UserDetailsServiceImpl
實(shí)現(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); //如果沒(méi)有查詢到用戶,就拋出異常 if(Objects.isNull(user)){ throw new RuntimeException("用戶名或者密碼錯(cuò)誤!"); } //將數(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
實(shí)現(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) { //進(jìn)行用戶認(rèn)證 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword()); Authentication authenticate = authenticationManager.authenticate(authenticationToken); //認(rèn)證未通過(guò),給出提示 if(Objects.isNull(authenticate)){ throw new RuntimeException("登陸失敗!"); } //通過(guò)了,生成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> { }
啟動(dòng)類
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); } }
效果展示
注意:測(cè)試時(shí)要打開(kāi)redis
,數(shù)據(jù)庫(kù)中的密碼必須是加密后的
可以使用一下方法進(jìn)行加密,passwordEncoder
是SecurityConfig
中已經(jīng)注入的
@Resource private PasswordEncoder passwordEncoder; String encode = passwordEncoder.encode("123456"); System.out.println(encode);
登錄接口測(cè)試
到此這篇關(guān)于Spring-Security實(shí)現(xiàn)登錄接口流程的文章就介紹到這了,更多相關(guān)Spring-Security登錄接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Spring?Cloud?Stream處理Java消息流的操作流程
Spring?Cloud?Stream是一個(gè)用于構(gòu)建消息驅(qū)動(dòng)微服務(wù)的框架,能夠與各種消息中間件集成,如RabbitMQ、Kafka等,今天我們來(lái)探討如何使用Spring?Cloud?Stream來(lái)處理Java消息流,需要的朋友可以參考下2024-08-08Java多線程實(shí)現(xiàn)Runnable方式
這篇文章主要為大家詳細(xì)介紹了Java多線程如何實(shí)現(xiàn)Runnable方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03Spring Security中的Servlet過(guò)濾器體系代碼分析
這篇文章主要介紹了Spring Security中的Servlet過(guò)濾器體系,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07idea下如何設(shè)置項(xiàng)目啟動(dòng)的JVM運(yùn)行內(nèi)存大小
這篇文章主要介紹了idea下如何設(shè)置項(xiàng)目啟動(dòng)的JVM運(yùn)行內(nèi)存大小問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12Spring?cloud?Hystrix注解初始化源碼過(guò)程解讀
這篇文章主要為大家介紹了Hystrix初始化部分,我們從源碼的角度分析一下@EnableCircuitBreaker以及@HystrixCommand注解的初始化過(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-12-12Java零基礎(chǔ)教程之Windows下安裝 JDK的方法圖解
這篇文章主要介紹了Java零基礎(chǔ)教程之Windows下安裝 JDK的方法圖解,本文介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實(shí)例解析
這篇文章主要介紹了IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07