当前位置: 首页 > wzjs >正文

丽水市建设局网站搜索热门关键词

丽水市建设局网站,搜索热门关键词,dw网页编辑器,wordpress淘宝客主题lkkbk文章目录1 简介1.1 认证和授权1.2 spring security的核心过滤器链1.3 入门案例认证流程1.4 自定义SpringSecurity认证、授权、校验1.4.1 登录1.4.2 校验2 快速入门2.1 pom引入依赖2.2 创建domain包2.2.1 新建AjaxResult2.2.2 新建User类2.3 创建common包2.3.1 创建HttpStatus类…

文章目录

  • 1 简介
  • 1.1 认证和授权
  • 1.2 spring security的核心过滤器链
  • 1.3 入门案例认证流程
  • 1.4 自定义SpringSecurity认证、授权、校验
    • 1.4.1 登录
    • 1.4.2 校验
  • 2 快速入门
    • 2.1 pom引入依赖
    • 2.2 创建domain包
      • 2.2.1 新建AjaxResult
      • 2.2.2 新建User类
    • 2.3 创建common包
      • 2.3.1 创建HttpStatus类
    • 2.4 创建config包
      • 2.4.1 创建RedisConfig类
    • 2.5 创建utils包
      • 2.5.1 创建FastJson2JsonRedisSerializer包
      • 2.5.2 创建JwtUtils类
      • 2.5.3 创建RedisCache类
      • 2.5.4 创建WebUtils类
    • 2.6 使用mybatis-plus连接数据库
  • 3 实现UserDetailsService,重写loadUserByUsername方法,替换InMemoryUserDetailsManager的loadUserByUsername方法
    • 3.1 在domain包下新建LoginUser并实现UserDetails
    • 3.2 在service包下新建UserDetailsServiceImpl并实现UserDetailsService
  • 4 使用BCryptPasswordEncoder进行密码校验方式
  • 5 自定义登录接口
    • 5.1 新建LoginController
    • 5.2 SecurityConfig中添加代码
    • 5.3 新建接口LoginService和LoginServiceImpl实现类
  • 6 自定义JWT认证过滤器
    • 6.1 新建JwtAuthenticationTokenFilter并实现OncePerRequestFilter
    • 6.2 添加JWT filter 到 UsernamePasswordAuthenticationFilter 之前
  • 7 退出登录
    • 7.1 新增登出方法
    • 7.2 新增登出逻辑
  • 8 授权实现
    • 8.1 LoginUser新增属性
    • 8.2 查询对应的权限信息
    • 8.3 JwtAuthenticationTokenFilter封装权限信息到Authentication中
  • 9 代码地址

1 简介

1.1 认证和授权

认证和授权是SpringSecurity作为安全框架的核心功能

  • 认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户
  • 授权:经过认证后判断当前用户是否有权限进行某个操作
  • 校验:再次访问时,判断是否有token

1.2 spring security的核心过滤器链

  • UsernamePasswordAuthenticationFilter:负责处理我们在登录页面填写了用户名密码后的登录请求。
  • ExceptionTranslationFilter:处理过滤器链中抛出的任何AccessDeniedExceptionAuthenticationException
  • FilterSecurityInterceptor:负责权限校验的过滤器。

1.3 入门案例认证流程

  • 1、UsernamePasswordAuthenticationFilter将用户名和密码封装于Authentication,此时,对象内只有用户名和密码,还没有权限信息
  • 2、调用authenticate方法进行认证,传到ProviderManager
  • 3、再调用DaoAuthenticationProviderauthenticate方法进行认证
  • 4、再调用InMemoryUserDetailsManagerloadUserByUsername方法去内存中查询用户及其权限,并将用户信息和权限封装成UserDetails对象返回到DaoAuthenticationProvider
  • 5、通过PasswordEncoder对比UserDetailsAuthentication的密码是否正确,如果正确就把权限信息设置到Authentication返回到UsernamePasswordAuthenticationFilter
  • 6、如果在UsernamePasswordAuthenticationFilter返回了Authentication对象就使用SecurityContextHolder.getContext().setAuthentication()方法储存该对象。其他过滤器可以通过SecurityContextHolder来获取当前用户信息。

1.4 自定义SpringSecurity认证、授权、校验

1.4.1 登录

  • 自定义登录接口:调用ProviderManager的方法进行认证如果认证通过生成jwt,并把用户信息存入redis
  • 自定义UserDetailsService,在这个实现类中去查询数据库用户信息

1.4.2 校验

定义jwt认证过滤器

  • 获取token
  • 解析token获取其中的userid
  • 从redis中获取用户信息
  • 存入SecurityContextHolder

2 快速入门

2.1 pom引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 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.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.20</version></dependency><!-- jwt依赖 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency><!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!-- mysql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

2.2 创建domain包

2.2.1 新建AjaxResult

package com.yunfeng.tokendemo.domain;import com.yunfeng.tokendemo.common.HttpStatus;
import org.springframework.util.ObjectUtils;import java.util.HashMap;/*** 操作消息提醒*/
public class AjaxResult extends HashMap<String, Object>
{private static final long serialVersionUID = 1L;/** 状态码 */public static final String CODE_TAG = "code";/** 返回内容 */public static final String MSG_TAG = "msg";/** 数据对象 */public static final String DATA_TAG = "data";/*** 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。*/public AjaxResult(){}/*** 初始化一个新创建的 AjaxResult 对象** @param code 状态码* @param msg 返回内容*/public AjaxResult(int code, String msg){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}/*** 初始化一个新创建的 AjaxResult 对象** @param code 状态码* @param msg 返回内容* @param data 数据对象*/public AjaxResult(int code, String msg, Object data){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (ObjectUtils.isEmpty(data)) {super.put(DATA_TAG, data);}}/*** 返回成功消息** @return 成功消息*/public static AjaxResult success(){return AjaxResult.success("操作成功");}/*** 返回成功数据** @return 成功消息*/public static AjaxResult success(Object data){return AjaxResult.success("操作成功", data);}/*** 返回成功消息** @param msg 返回内容* @return 成功消息*/public static AjaxResult success(String msg){return AjaxResult.success(msg, null);}/*** 返回成功消息** @param msg 返回内容* @param data 数据对象* @return 成功消息*/public static AjaxResult success(String msg, Object data){return new AjaxResult(HttpStatus.SUCCESS, msg, data);}/*** 返回警告消息** @param msg 返回内容* @return 警告消息*/public static AjaxResult warn(String msg){return AjaxResult.warn(msg, null);}/*** 返回警告消息** @param msg 返回内容* @param data 数据对象* @return 警告消息*/public static AjaxResult warn(String msg, Object data){return new AjaxResult(HttpStatus.WARN, msg, data);}/*** 返回错误消息** @return 错误消息*/public static AjaxResult error(){return AjaxResult.error("操作失败");}/*** 返回错误消息** @param msg 返回内容* @return 错误消息*/public static AjaxResult error(String msg){return AjaxResult.error(msg, null);}/*** 返回错误消息** @param msg 返回内容* @param data 数据对象* @return 错误消息*/public static AjaxResult error(String msg, Object data){return new AjaxResult(HttpStatus.ERROR, msg, data);}/*** 返回错误消息** @param code 状态码* @param msg 返回内容* @return 错误消息*/public static AjaxResult error(int code, String msg){return new AjaxResult(code, msg, null);}/*** 方便链式调用** @param key 键* @param value 值* @return 数据对象*/@Overridepublic AjaxResult put(String key, Object value){super.put(key, value);return this;}
}

2.2.2 新建User类

package com.yunfeng.tokendemo.domain;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;import java.io.Serializable;
import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User implements Serializable {private static final long serialVersionUID = -403567854238683121L;/*** 主键*/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;
}

2.3 创建common包

2.3.1 创建HttpStatus类

package com.yunfeng.tokendemo.common;/*** 返回状态码*/
public class HttpStatus
{/*** 操作成功*/public static final int SUCCESS = 200;/*** 对象创建成功*/public static final int CREATED = 201;/*** 请求已经被接受*/public static final int ACCEPTED = 202;/*** 操作已经执行成功,但是没有返回数据*/public static final int NO_CONTENT = 204;/*** 资源已被移除*/public static final int MOVED_PERM = 301;/*** 重定向*/public static final int SEE_OTHER = 303;/*** 资源没有被修改*/public static final int NOT_MODIFIED = 304;/*** 参数列表错误(缺少,格式不匹配)*/public static final int BAD_REQUEST = 400;/*** 未授权*/public static final int UNAUTHORIZED = 401;/*** 访问受限,授权过期*/public static final int FORBIDDEN = 403;/*** 资源,服务未找到*/public static final int NOT_FOUND = 404;/*** 不允许的http方法*/public static final int BAD_METHOD = 405;/*** 资源冲突,或者资源被锁*/public static final int CONFLICT = 409;/*** 不支持的数据,媒体类型*/public static final int UNSUPPORTED_TYPE = 415;/*** 系统内部错误*/public static final int ERROR = 500;/*** 接口未实现*/public static final int NOT_IMPLEMENTED = 501;/*** 系统警告消息*/public static final int WARN = 601;
}

2.4 创建config包

2.4.1 创建RedisConfig类

package com.yunfeng.tokendemo.config;import com.yunfeng.tokendemo.utils.FastJson2JsonRedisSerializer;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** redis配置*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(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;}@Beanpublic DefaultRedisScript<Long> limitScript(){DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptText(limitScriptText());redisScript.setResultType(Long.class);return redisScript;}/*** 限流脚本*/private String limitScriptText(){return "local key = KEYS[1]\n" +"local count = tonumber(ARGV[1])\n" +"local time = tonumber(ARGV[2])\n" +"local current = redis.call('get', key);\n" +"if current and tonumber(current) > count then\n" +"    return tonumber(current);\n" +"end\n" +"current = redis.call('incr', key)\n" +"if tonumber(current) == 1 then\n" +"    redis.call('expire', key, time)\n" +"end\n" +"return tonumber(current);";}
}

2.5 创建utils包

2.5.1 创建FastJson2JsonRedisSerializer包

package com.yunfeng.tokendemo.utils;import java.nio.charset.Charset;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;/*** Redis使用FastJson序列化*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");private Class<T> clazz;public FastJson2JsonRedisSerializer(Class<T> clazz){super();this.clazz = clazz;}@Overridepublic byte[] serialize(T t) throws SerializationException{if (t == null){return new byte[0];}return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);}@Overridepublic 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, JSONReader.Feature.SupportAutoType);}
}

2.5.2 创建JwtUtils类

package com.yunfeng.tokendemo.utils;import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;/*** Jwt工具类*/
public class JwtUtils
{public static Long JWT_TTL = 60*60*1000l; //一个小时/*** 令牌秘钥*/public final static String secret = "abcdefghijklmnopqrstuvwxyz";/*** 用户ID字段*/public static final String DETAILS_USER_ID = "user_id";/*** 登录用户*/public static final String LOGIN_USER = "login_user";/*** 用户标识*/public static final String USER_KEY = "user_key";/*** 用户名字段*/public static final String DETAILS_USERNAME = "username";/*** 从数据声明生成令牌** @param claims 数据声明* @return 令牌*/public static String createToken(Map<String, Object> claims){String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();return token;}/*** 从令牌中获取数据声明** @param token 令牌* @return 数据声明*/public static Claims parseToken(String token){return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}/*** 根据令牌获取用户标识** @param token 令牌* @return 用户ID*/public static String getUserKey(String token){Claims claims = parseToken(token);return getValue(claims, USER_KEY);}/*** 根据令牌获取用户标识** @param claims 身份信息* @return 用户ID*/public static String getUserKey(Claims claims){return getValue(claims, USER_KEY);}/*** 根据令牌获取用户ID** @param token 令牌* @return 用户ID*/public static String getUserId(String token){Claims claims = parseToken(token);return getValue(claims, DETAILS_USER_ID);}/*** 根据身份信息获取用户ID** @param claims 身份信息* @return 用户ID*/public static String getUserId(Claims claims){return getValue(claims, DETAILS_USER_ID);}/*** 根据令牌获取用户名** @param token 令牌* @return 用户名*/public static String getUserName(String token){Claims claims = parseToken(token);return getValue(claims, DETAILS_USERNAME);}/*** 根据身份信息获取用户名** @param claims 身份信息* @return 用户名*/public static String getUserName(Claims claims){return getValue(claims, DETAILS_USERNAME);}/*** 根据身份信息获取键值** @param claims 身份信息* @param key 键* @return 值*/public static String getValue(Claims claims, String key){Object value = claims;String defaultValue = key;if (null == value){return defaultValue;}if (value instanceof String){return (String) value;}return value.toString();}
}

2.5.3 创建RedisCache类

package com.yunfeng.tokendemo.utils;import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;/*** spring redis 工具类**/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{@Autowiredpublic 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 Redis键* @return 有效时间*/public long getExpire(final String key){return redisTemplate.getExpire(key);}/*** 判断 key是否存在** @param key 键* @return true 存在 false不存在*/public Boolean hasKey(String key){return redisTemplate.hasKey(key);}/*** 获得缓存的基本对象。** @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 boolean deleteObject(final Collection collection){return redisTemplate.delete(collection) > 0;}/*** 缓存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 Redis键* @param hKeys Hash键集合* @return Hash对象集合*/public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys){return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 删除Hash中的某条数据** @param key Redis键* @param hKey Hash键* @return 是否成功*/public boolean deleteCacheMapValue(final String key, final String hKey){return redisTemplate.opsForHash().delete(key, hKey) > 0;}/*** 获得缓存的基本对象列表** @param pattern 字符串前缀* @return 对象列表*/public Collection<String> keys(final String pattern){return redisTemplate.keys(pattern);}
}

2.5.4 创建WebUtils类

package com.yunfeng.tokendemo.utils;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().println(string);} catch (IOException e) {e.printStackTrace();}return null;}
}

2.6 使用mybatis-plus连接数据库

参考:https://blog.csdn.net/weixin_43684214/article/details/128007977

3 实现UserDetailsService,重写loadUserByUsername方法,替换InMemoryUserDetailsManager的loadUserByUsername方法

3.1 在domain包下新建LoginUser并实现UserDetails

package com.yunfeng.tokendemo.domain;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;}
}

3.2 在service包下新建UserDetailsServiceImpl并实现UserDetailsService

package com.yunfeng.tokendemo.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yunfeng.tokendemo.domain.LoginUser;
import com.yunfeng.tokendemo.domain.User;
import com.yunfeng.tokendemo.mapper.UserMapper;
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;import java.util.Objects;@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic 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 查询对应的权限信息//把数据封装成UserDetails返回return new LoginUser(user);}
}

4 使用BCryptPasswordEncoder进行密码校验方式

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
}

5 自定义登录接口

5.1 新建LoginController

package com.yunfeng.tokendemo.controller;import com.yunfeng.tokendemo.domain.AjaxResult;
import com.yunfeng.tokendemo.domain.User;
import com.yunfeng.tokendemo.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class LoginController {@Autowiredprivate LoginService loginService;@PostMapping("/user/login")public AjaxResult login(@RequestBody User user){//登录return loginService.login(user);}}

5.2 SecurityConfig中添加代码

 @Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** Override this method to configure the {@link HttpSecurity}. Typically subclasses* should not invoke this method by calling super as it may override their* configuration. The default configuration is:** <pre>* http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();* </pre>* <p>* Any endpoint that requires defense against common vulnerabilities can be specified* here, including public ones. See {@link HttpSecurity#authorizeRequests} and the* `permitAll()` authorization rule for more details on public endpoints.** @param http the {@link HttpSecurity} to modify* @throws Exception if an error occurs*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http    //CSRF禁用,因为不使用session.csrf().disable()//基于token,所以不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//授权请求.authorizeRequests()//对于登录login 允许匿名访问.antMatchers("/user/login").anonymous()//除上面所有请求都要鉴权认证.anyRequest().authenticated();}

5.3 新建接口LoginService和LoginServiceImpl实现类

  • 接口
package com.yunfeng.tokendemo.service;import com.yunfeng.tokendemo.domain.AjaxResult;
import com.yunfeng.tokendemo.domain.User;public interface LoginService {AjaxResult login(User user);
}
  • 实现类
package com.yunfeng.tokendemo.service.impl;import com.yunfeng.tokendemo.domain.AjaxResult;
import com.yunfeng.tokendemo.domain.LoginUser;
import com.yunfeng.tokendemo.domain.User;
import com.yunfeng.tokendemo.service.LoginService;
import com.yunfeng.tokendemo.utils.JwtUtils;
import com.yunfeng.tokendemo.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
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;@Service
public class LoginServiceImpl implements LoginService {@Resourceprivate AuthenticationManager authenticationManager;@Autowiredprivate RedisCache redisCache;@Overridepublic AjaxResult login(User user) {AjaxResult ajax = AjaxResult.success();//AuthenticationManager authenticate 进行用户认证UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());Authentication authenticate = authenticationManager.authenticate(authenticationToken);//如果认证没通过,给出对应的提示if (Objects.isNull(authenticate)){throw new RuntimeException("登录失败");}//如果认证通过了,使用userid生成一个jwt jwt存入ajaxLoginUser loginUser = (LoginUser) authenticate.getPrincipal();Long userId = loginUser.getUser().getId();Map<String,Object> map = new HashMap<>();map.put(JwtUtils.DETAILS_USER_ID,userId);String token = JwtUtils.createToken(map);ajax.put("token",token);//把完整的用户信息存入redis userid作为keyredisCache.setCacheObject("login:"+userId,loginUser);return ajax;}
}

6 自定义JWT认证过滤器

6.1 新建JwtAuthenticationTokenFilter并实现OncePerRequestFilter

package com.yunfeng.tokendemo;import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.yunfeng.tokendemo.domain.LoginUser;
import com.yunfeng.tokendemo.utils.JwtUtils;
import com.yunfeng.tokendemo.utils.RedisCache;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
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.Map;
import java.util.Objects;@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate RedisCache redisCache;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//获取tokenString token = request.getHeader("token");if (!StringUtils.hasText(token)) {//放行filterChain.doFilter(request,response);return;}//解析tokenString userId = JwtUtils.getUserId(token);//从redis中获取用户信息userId = userId.substring(userId.indexOf("=") + 1 , userId.indexOf("}"));String redisKey = "login:" + userId;LoginUser loginUser = redisCache.getCacheObject(redisKey);if (Objects.isNull(loginUser)){throw new RuntimeException("用户未登录!");}//存入SecurityContextHolder// TODO 获取权限信息封装到Authentication中UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =new UsernamePasswordAuthenticationToken(loginUser,null,null);SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);//放行filterChain.doFilter(request,response);}
}

6.2 添加JWT filter 到 UsernamePasswordAuthenticationFilter 之前

  • 在SecurityConfig中添加代码
	@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;@Overrideprotected void configure(HttpSecurity http) throws Exception {http    //CSRF禁用,因为不使用session.csrf().disable()//基于token,所以不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//授权请求.authorizeRequests()//对于登录login 允许匿名访问.antMatchers("/user/login").anonymous()//除上面所有请求都要鉴权认证.anyRequest().authenticated();// 添加JWT filter 到 UsernamePasswordAuthenticationFilter 之前http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);}

7 退出登录

7.1 新增登出方法

@RequestMapping("/user/logout")public AjaxResult logout(){return loginService.logout();}

7.2 新增登出逻辑

@Overridepublic AjaxResult logout() {//获取SecirityContextHolder中的用户idUsernamePasswordAuthenticationToken authentication =(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();LoginUser loginUser = (LoginUser) authentication.getPrincipal();Long id = loginUser.getUser().getId();//删除redis中的值redisCache.deleteObject("login:" + id);return AjaxResult.success();}

8 授权实现

8.1 LoginUser新增属性

private List<String> permissions;@JSONField(serialize = false)private List<SimpleGrantedAuthority> authorities;public LoginUser(User user, List<String> permissions) {this.user = user;this.permissions = permissions;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if (authorities!=null){return authorities;}//把permissions中的String类型的权限信息封装成return permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}

8.2 查询对应的权限信息

  • 在loadUserByUsername中设置权限信息
 		//  查询对应的权限信息List<String> list = new ArrayList<>(Arrays.asList("test","admin"));//把数据封装成UserDetails返回return new LoginUser(user,list);

8.3 JwtAuthenticationTokenFilter封装权限信息到Authentication中

		LoginUser loginUser = redisCache.getCacheObject(redisKey);if (Objects.isNull(loginUser)){throw new RuntimeException("用户未登录!");}//  获取权限信息封装到Authentication中UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =new UsernamePasswordAuthenticationToken(loginUser,null, loginUser.getAuthorities());

9 代码地址

想看代码的可以看一下,都是简单的入门案例!
https://gitee.com/logicfeng/springsecurity-jwt.git

http://www.dtcms.com/wzjs/112664.html

相关文章:

  • 小程序商家入驻平台seo神器
  • 创建网站要钱吗产品推销
  • dedecms小说网站模板优化方案模板
  • 怎样用自己的服务器做网站广东公共广告20120708
  • 荣誉章标志做网站百度官方版下载
  • 购物网站 app西安网站建设公司排名
  • 机关门户网站app建设思考站外seo是什么
  • 淘宝网站开发用到哪些技术优化设计答案六年级上册语文
  • 网站常用布局seo sem是指什么意思
  • 建设电商网站哪个平台比较好2021年度关键词有哪些
  • 重庆网站设计公司网站制作网络推广
  • 网站建设的社会效益可行性分析代理公司注册
  • 天津市建设厅政府网站商家推广平台有哪些
  • 公司备案证查询网站查询网站查询今天重大新闻国内最新消息
  • 阿里云服务器上做网站湖南长沙疫情最新消息
  • 网站整站程序智能网站排名优化
  • 手机和电脑网站分开做seo入门到精通
  • 做苗木网站百度pc版网页
  • 郑州小程序开发制作公司天津seo标准
  • 广告发布网站开发什么是搜索引擎优化?
  • 网站做选择题怎么快速选择爱站站长工具
  • 郴州市做网站海南网站制作
  • 网站建设基础教程人教版seo整站优化解决方案
  • 九江城乡建设网站为什么打不开网站怎么优化推广
  • 西安做网站要多少钱星力游戏源码
  • 我自己做网站app推广公司
  • 六枝做网站焦作网站seo
  • 网页都有哪些seo专家是什么意思
  • asp.net做新闻网站模板简述网站推广的方法
  • 昆明网站建设方案外包百度网盘资源链接入口