专门做美食的网站6,集团网站建设网络公司,什么网站能让小孩做算术题,wordpress网站插件下载失败文章目录登陆校验流程依赖yaml实现建表、工具类、实体类加密器、AuthenticationManager登录逻辑登录过滤器、配置过滤器登出登陆校验流程
认证 登录#xff1a; ①自定义登录接口 调用ProviderManager的方法进行认证 如果认证通过生成token#xff0c;根据userId把用…
文章目录登陆校验流程依赖yaml实现建表、工具类、实体类加密器、AuthenticationManager登录逻辑登录过滤器、配置过滤器登出登陆校验流程
认证 登录 ①自定义登录接口 调用ProviderManager的方法进行认证 如果认证通过生成token根据userId把用户信息存入redis中,返回token给前端 ②自定义UserDetailsService 在这个实现类中去查询数据库封装为UserDetails对象返回
校验 ①定义Jwt认证过滤器 获取token 解析token获取其中的userid 从redis中获取用户信息 存入SecurityContextHolder
依赖
dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.4.3/version
/dependency
dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId
/dependency!--redis依赖--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependency
!--fastjson依赖--
dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.33/version
/dependency
!--jwt依赖--
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.0/version
/dependency!-- lombok --
dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId
/dependency!-- security--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId
/dependencyyaml
spring:datasource:url: jdbc:mysql://192.168.111.101:3306/security?characterEncodingutf-8serverTimezoneAsia/Shanghaiusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver实现
建表、工具类、实体类
建表语句 CREATE TABLE 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)
) ENGINEINNODB AUTO_INCREMENT2 DEFAULT CHARSETutf8mb4 COMMENT用户表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 xd52s8w;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(ttlMillisnull){ttlMillisJwtUtil.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();}}实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;/*** 用户表(User)实体类*/
Data
AllArgsConstructor
NoArgsConstructor
public class User implements Serializable {private static final long serialVersionUID 1L;/*** 主键*/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;
}UserDetails loadUserByUsername 方法返回的数据
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;Data
AllArgsConstructor
NoArgsConstructor
public class LoginUser implements UserDetails {private User user;Overridepublic Collection? extends GrantedAuthority getAuthorities() {return null;}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;}
}
UserDetailsService 通过loadUserByUsername获取用户封装成UserDetails 对象返回
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.security.mapper.UserMapper;
import com.example.security.vo.LoginUser;
import com.example.security.vo.User;
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;Service
public class UserDetailsServiceImpl implements UserDetailsService {Autowiredprivate UserMapper userMapper;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 查询用户信息LambdaQueryWrapperUser queryWrapper new LambdaQueryWrapper ();queryWrapper.eq (User::getUserName, username);User user userMapper.selectOne (queryWrapper);if (user null) {// 用户不存在throw new RuntimeException (用户不存在);}//TODO 根据用户查询权限信息 添加到LoginUser中return new LoginUser (user);}
}
加密器、AuthenticationManager
密码加密存储、登陆接口
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;Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 密码加密存储** return*/Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder ();}Overrideprotected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf ().disable ()//不通过Session获取SecurityContext.sessionManagement ().sessionCreationPolicy (SessionCreationPolicy.STATELESS).and ().authorizeRequests ()// 对于登录接口 允许匿名访问.antMatchers (/user/login).anonymous ()// 除上面外的所有请求 全部需要鉴权认证.anyRequest ().authenticated ();}/*** 登陆接口* 通过AuthenticationManager的authenticate方法来进行用户认证* 所以需要在SecurityConfig中配置把AuthenticationManager注入容器** return* throws Exception*/BeanOverridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean ();}
}登录逻辑
LoginServiceImpl 登录逻辑
import com.alibaba.fastjson.JSONObject;
import com.example.security.config.JwtUtil;
import com.example.security.vo.LoginUser;
import com.example.security.vo.ResponseResult;
import com.example.security.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Objects;Service
public class LoginServiceImpl implements LoginServcie {Autowiredprivate AuthenticationManager authenticationManager;Autowiredprivate StringRedisTemplate redisTemplate;Overridepublic 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生成tokenLoginUser loginUser (LoginUser) authenticate.getPrincipal ();String userId loginUser.getUser ().getId ().toString ();String token JwtUtil.createJWT (userId);// authenticate存入redisredisTemplate.opsForValue ().set (login: userId, JSONObject.toJSONString (loginUser));// 把token响应给前端HashMapString, String map new HashMap ();map.put (token, token);return new ResponseResult (200, 登陆成功, map);}
}
登录过滤器、配置过滤器
对需要登录的接口进行过滤从redis中查询用户信息未登录就 不放行
import com.alibaba.fastjson.JSONObject;
import com.example.security.vo.LoginUser;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {Autowiredprivate StringRedisTemplate redisTemplate;/*** 对于需要登录的接口进行拦截* 看看用户信息是否存在** param request* param response* param filterChain* throws ServletException* throws IOException*/Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 获取 tokenString token request.getHeader (token);if (!StringUtils.hasText (token)) {// 不携带token,放行filterChain.doFilter (request, response);return;}//解析tokenString userId;try {Claims claims JwtUtil.parseJWT (token);userId claims.getSubject ();} catch (Exception e) {e.printStackTrace ();throw new RuntimeException (token非法);}//从redis中获取用户信息String redisKey login: userId;LoginUser loginUser JSONObject.parseObject (redisTemplate.opsForValue ().get (redisKey), LoginUser.class);if (Objects.isNull (loginUser)) {// 没有token就是未登录throw new RuntimeException (用户未登录);}//存入SecurityContextHolder//TODO 获取权限信息封装到Authentication中UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken (loginUser, null, null);SecurityContextHolder.getContext ().setAuthentication (authenticationToken);// 放行filterChain.doFilter (request, response);}
}配置过滤器使其生效 Autowiredprivate JwtAuthenticationTokenFilter filter;Overrideprotected void configure(HttpSecurity http) throws Exception {// 把token校验过滤器 添加到过滤器链中http.addFilterBefore (filter, UsernamePasswordAuthenticationFilter.class);http//关闭csrf.csrf ().disable ()//不通过Session获取SecurityContext.sessionManagement ().sessionCreationPolicy (SessionCreationPolicy.STATELESS).and ().authorizeRequests ()// 对于登录接口 允许匿名访问.antMatchers (/user/login).anonymous ()// 除上面外的所有请求 全部需要鉴权认证.anyRequest ().authenticated ();}登出
将用户信息从Redis中删除 登出 public ResponseResult logout() {Authentication authentication SecurityContextHolder.getContext ().getAuthentication ();LoginUser loginUser (LoginUser) authentication.getPrincipal ();Long userid loginUser.getUser ().getId ();redisTemplate.delete (login: userid);return new ResponseResult (200, 退出成功);
}