SpringSecurity_分离
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SpringSecurity_test</artifactId>
<version>1.0-SNAPSHOT</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class,args);
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
服务器获取token并解析,看用户是否拥有相关的权限,如果有则进行下一步的操作
UsenamepasswordauthenticationFilter
Authentication接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。
AuthenticationManager接口:定义了认证Authentication的方法
UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。
UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。
调用ProviderManage的方法进行认证 如果认证通过则生成JWT
4.封装Authentication并存入SecurityContexHolder对象
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<groupId>io.jsonwebtoken</groupId>
url: jdbc:mysql://localhost:3306/springsecurity?characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
`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代表已删除)',
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户表'
public static String renderString(HttpServletResponse response, String string) {
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print(string);
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate redisTemplate;
public <T> void setCacheObject(final String key, final T value)
redisTemplate.opsForValue().set(key, value);
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
* @return true=设置成功;false=设置失败
public boolean expire(final String key, final long timeout)
return expire(key, timeout, TimeUnit.SECONDS);
* @return true=设置成功;false=设置失败
public boolean expire(final String key, final long timeout, final TimeUnit unit)
return redisTemplate.expire(key, timeout, unit);
public <T> T getCacheObject(final String key)
ValueOperations<String, T> operation = redisTemplate.opsForValue();
public boolean deleteObject(final String key)
return redisTemplate.delete(key);
public long deleteObject(final Collection collection)
return redisTemplate.delete(collection);
public <T> long setCacheList(final String key, final List<T> dataList)
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
public <T> List<T> getCacheList(final String key)
return redisTemplate.opsForList().range(key, 0, -1);
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();
public <T> Set<T> getCacheSet(final String key)
return redisTemplate.opsForSet().members(key);
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
redisTemplate.opsForHash().putAll(key, dataMap);
public <T> Map<String, T> getCacheMap(final String key)
return redisTemplate.opsForHash().entries(key);
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
redisTemplate.opsForHash().put(key, hKey, value);
public <T> T getCacheMapValue(final String key, final String hKey)
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
public void delCacheMapValue(final String key, final String hkey)
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hkey);
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
return redisTemplate.opsForHash().multiGet(key, hKeys);
public Collection<String> keys(final String pattern)
return redisTemplate.keys(pattern);
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000 一个小时
public static final String JWT_KEY = "sangeng";
public static String getUUID(){
String token = UUID.randomUUID().toString().replaceAll("-", "");
* @param subject token中要存放的数据(json格式)
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
* @param subject token中要存放的数据(json格式)
public static String createJWT(String subject, Long ttlMillis) {
JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
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);
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
.setSubject(subject) // 主题 可以是JSON数据
.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
public static String createJWT(String id, String subject, Long ttlMillis) {
JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
public static void main(String[] args) throws Exception {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";
Claims claims = parseJWT(token);
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
public FastJsonRedisSerializer(Class<T> clazz) {
public byte[] serialize(Object o) throws SerializationException {
return JSON.toJSONString(o, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
public JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
public static String renderString(HttpServletResponse response, String string) {
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print(string);
public class HelloController {
在service层实现UserdetailsService接口实现用户的数据库认证
public class UserDetailsServiceImpl implements UserDetailsService {
private UserMapper userMapper;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LambdaQueryWrapper<User> lambdaQueryWrapper =new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUserName,username);
User user = userMapper.selectOne(lambdaQueryWrapper);
throw new RuntimeException("没有查询到用户");
public interface UserMapper extends BaseMapper<User> {
实现UserDetail实体类并UserDetailService返回给框架验证
public class LoginUser implements UserDetails {
public Collection<? extends GrantedAuthority> getAuthorities() {
public boolean isAccountNonExpired() {
public boolean isAccountNonLocked() {
public boolean isCredentialsNonExpired() {
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> {
public ResponseResult(Integer code, String msg) {
public ResponseResult(Integer code, T data) {
public void setCode(Integer code) {
public void setMsg(String msg) {
public ResponseResult(Integer code, String msg, T data) {
public class User implements Serializable {
private static final long serialVersionUID = -40356785423868312L;
密码在SpringSecurity中一般是加密在数据库中存储的,如果你需要明文处理需要在数据库中加上{noop}+密码的方式存储
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;
public class SecurityConfig extends WebSecurityConfigurerAdapter {
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
public static void main(String[] args) throws Exception {
String jwt = createJWT("1234");
String subject = claims.getSubject();
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;
import zou.bean.ResponseResult;
import zou.service.LoginService;
public class LoginController {
private LoginService loginService;
public ResponseResult login(@RequestBody User user){
ResponseResult result = loginService.login(user);
注意在SpringSecurity的配置类中将AuthenticationManager加入容器中并暴露
public class SecurityConfig extends WebSecurityConfigurerAdapter {
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
protected void configure(HttpSecurity http) throws Exception {
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.antMatchers("/user/login").anonymous()
.anyRequest().authenticated();
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
public interface LoginService {
ResponseResult login(User user);
public class LoginServiceImpl implements LoginService {
private AuthenticationManager authenticationManager;
private RedisCache redisCache;
public ResponseResult login(User user) {
//AuthenticationManager中进行用户认证
UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
if(Objects.isNull(authenticate)){
throw new RuntimeException("登录失败");
//如果认证通过了将用户信息存入redis把userid作为key
LoginUser loginUser = (LoginUser)authenticate.getPrincipal();
String user_id = loginUser.getUser().getId().toString();
String jwt = JwtUtil.createJWT(user_id);
Map<String,String> map =new HashMap<>();
redisCache.setCacheObject("login"+user_id,loginUser);
return new ResponseResult(200,"登陆成功",map);
2.8.1 JwtAuthenticationTokenFilter的编写
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private RedisCache redisCache;
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("token");
if(!StringUtils.hasText(token)){
filterChain.doFilter(request,response);
Claims claims = JwtUtil.parseJWT(token);
throw new RuntimeException("token非法");
String redisKey = "login:"+userId;
LoginUser loginUser = redisCache.getCacheObject(redisKey);
if(Objects.isNull(loginUser)){
throw new RuntimeException("用户未登录!");
// TODO 获取权限信息封装到AuthenticationToken中
UsernamePasswordAuthenticationToken AuthenticationToken=new UsernamePasswordAuthenticationToken(loginUser,null,null);
SecurityContextHolder.getContext().setAuthentication(AuthenticationToken);
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import zou.filter.JwtAuthenticationTokenFilter;
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
protected void configure(HttpSecurity http) throws Exception {
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.antMatchers("/user/login").anonymous()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
我们需要定义一个接口,然后获取SecurityContextHolder中的认证信息,删除redis中对应的数据即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?