网络推广最好的网站有哪些wordpress只显示一个主题

news/2025/10/4 23:00:37/文章来源:
网络推广最好的网站有哪些,wordpress只显示一个主题,水利建筑工程网站,澄迈网站建设文章目录 SpringSecurity简介快速入门1.准备工作1.2引入SpringSecurity 认证1.登录校验流程2.原理2.1SpringSecurity完整流程2.2认证流程详解 3.解决问题3.1思路分析3.2准备工作3.3.实现3.3.1数据库校验用户3.3.2密码加密存储3.3.3登录接口3.3.4认证过滤器3.3.5退出登录 授权1.… 文章目录 SpringSecurity简介快速入门1.准备工作1.2引入SpringSecurity 认证1.登录校验流程2.原理2.1SpringSecurity完整流程2.2认证流程详解 3.解决问题3.1思路分析3.2准备工作3.3.实现3.3.1数据库校验用户3.3.2密码加密存储3.3.3登录接口3.3.4认证过滤器3.3.5退出登录 授权1.权限系统的作用2.授权基本流程3.授权实现3.1限制访问资源所需权限3.2封装权限信息3.3从数据库查询权限信息3.3.1RBAC权限模型3.3.2代码实现 自定义失败处理跨域其他权限校验方法自定义权限校验方法基于配置的权限控制CSRF认证成功处理器认证失败处理器登出成功处理器 SpringSecurity 简介 SpringSecurity是Spring家族中的一个安全管理框架相比与另外一个安全框架Shiro它提供了更丰富的功能社区资源也比Shiro丰富 一般来说大型项目都是使用SpringSecurity来做安全框架小项目有Shiro的比较多因为相比与SpringSecurityShiro的上手更加的简单 一般Web应用的需要进行认证和授权 认证验证当前访问系统的是不是本系统的用户并且确认具体是哪个用户授权经过认证后判断当前用户是否有权限进行某个操作 认证和授权是SpringSecurity作为安全框架的核心功能 快速入门 1.准备工作 ①搭建一个SpringBoot工程添加依赖 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.5.0/version /parentdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency /dependencies②创建启动类 package com.dy;import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication public class SecurityApplication {public static void main(String[] args) {SpringApplication.run(SecurityApplication.class,args);} }③创建controller package com.dy.controller;import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;RestController public class HelloController {RequestMapping(/hello)public String Hello(){return hello;} }1.2引入SpringSecurity 在SpringBoot项目中添加依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId /dependency引入依赖后尝试访问之前的接口就会自动跳转到一个SpriingSecurity的默认登录页面默认用户名是User密码输出在控制台 必须登录后才能对接口进行访问 认证 1.登录校验流程 2.原理 2.1SpringSecurity完整流程 SpringSecurity的原理其实就是一个过滤器链内部包含了提供各种功能的过滤器。这里我们可以看看 入门案例中的过滤器。 图中只展示了核心过滤器其它的非核心过滤器并没有在图中展示 UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请 求。入门案例的认证工作主要有它负责。 ExceptionTranslationFilter处理过滤器链中抛出的任何AccessDeniedException和 AuthenticationException 。 FilterSecurityInterceptor负责权限校验的过滤器。 我们可以通过Debug查看当前系统中SpringSecurity过滤器链中有哪些过滤器及它们的顺序 2.2认证流程详解 概念速查: Authentication接口: 它的实现类表示当前访问系统的用户封装了用户相关信息。AuthenticationManager接口定义了认证Authentication的方法UserDetailsService接口加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的 方法。UserDetails接口提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装 成UserDetails对象返回。然后将这些信息封装到Authentication对象中 3.解决问题 3.1思路分析 登录 ①自定义登录接口 调用ProviderManager的方法进行认证如果认证通过生成jwt把用户信息存入redis中 ②自定义UserDeatilsService 在这个实现类中去查询数据库 校验 ①定义jwt认证过滤器 获取token解析token获取其中的userid从redis中获取用户信息存入securityContextHolder 3.2准备工作 ①添加依赖 !--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②添加Redis相关配置 package com.dy.utils;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 FastJsonRedisSerializerT implements RedisSerializerT {public static final Charset DEFAULT_CHARSET Charset.forName(UTF-8);private ClassT clazz;static {ParserConfig.getGlobalInstance().setAutoTypeSupport(true);}public FastJsonRedisSerializer(ClassT clazz) {super();this.clazz clazz;}Overridepublic byte[] serialize(T t) throws SerializationException {if (t null) {return new byte[0];}return JSON.toJSONString(t,SerializerFeature.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);}protected JavaType getJavaType(Class? clazz) {return TypeFactory.defaultInstance().constructType(clazz);} }package com.dy.config;import com.dy.utils.FastJsonRedisSerializer; 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 {BeanSuppressWarnings(value {unchecked, rawtypes})public RedisTemplateObject, Object redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplateObject, Object template new RedisTemplate();template.setConnectionFactory(connectionFactory);FastJsonRedisSerializer serializer newFastJsonRedisSerializer(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;} }③响应类 package com.dy.domain;import com.fasterxml.jackson.annotation.JsonInclude;/*** Author 三更 B站 https://space.bilibili.com/663528522*/ JsonInclude(JsonInclude.Include.NON_NULL) public class ResponseResultT {/*** 状态码*/private Integer code;/*** 提示信息如果有错误时前端可以获取该字段进行提示*/private String msg;/*** 查询到的结果数据*/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;} }④工具类 package com.dy.utils;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 sangeng;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 (ttlMillis null) {ttlMillis JwtUtil.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();} }package com.dy.utils;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;import java.util.*; import java.util.concurrent.TimeUnit;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, finalInteger 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 TimeUnitunit) {return redisTemplate.expire(key, timeout, unit);}/*** 获得缓存的基本对象。** param key 缓存键值* return 缓存键值对应的数据*/public T T getCacheObject(final String key) {ValueOperationsString, 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数据** param key 缓存的键值* param dataList 待缓存的List数据* return 缓存的对象*/public T long setCacheList(final String key, final ListT dataList) {Long count redisTemplate.opsForList().rightPushAll(key, dataList);return count null ? 0 : count;}/*** 获得缓存的list对象** param key 缓存的键值* return 缓存键值对应的数据*/public T ListT getCacheList(final String key) {return redisTemplate.opsForList().range(key, 0, -1);}/*** 缓存Set** param key 缓存键值* param dataSet 缓存的数据* return 缓存数据的对象*/public T BoundSetOperationsString, T setCacheSet(final String key, finalSetT dataSet) {BoundSetOperationsString, T setOperation redisTemplate.boundSetOps(key);IteratorT it dataSet.iterator();while (it.hasNext()) {setOperation.add(it.next());}return setOperation;}/*** 获得缓存的set** param key* return*/public T SetT getCacheSet(final String key) {return redisTemplate.opsForSet().members(key);}/*** 缓存Map** param key* param dataMap*/public T void setCacheMap(final String key, final MapString, T dataMap) {if (dataMap ! null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 获得缓存的Map** param key* return*/public T MapString, 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, finalT 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) {HashOperationsString, String, T opsForHash redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 删除Hash中的数据** param key* param hkey*/public void delCacheMapValue(final String key, final String hkey) {HashOperations hashOperations redisTemplate.opsForHash();hashOperations.delete(key, hkey);}/*** 获取多个Hash中的数据** param key Redis键* param hKeys Hash键集合* return Hash对象集合*/public T ListT getMultiCacheMapValue(final String key, finalCollectionObject hKeys) {return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 获得缓存的基本对象列表** param pattern 字符串前缀* return 对象列表*/public CollectionString keys(final String pattern) {return redisTemplate.keys(pattern);} }package com.dy.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, Stringstring) {try {response.setStatus(200);response.setContentType(application/json);response.setCharacterEncoding(utf-8);response.getWriter().print(string);} catch (IOException e) {e.printStackTrace();}return null;} }⑤实体类 package com.dy.domain;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(value sys_user) public class User implements Serializable {private static final long serialVersionUID -40356785423868312L;/*** 主键*/TableIdprivate 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; }3.3.实现 3.3.1数据库校验用户 从之前的分析我们可以知道我们可以自定义一个UserDetailsService,让SpringSecurity使用我们的 UserDetailsService。我们自己的UserDetailsService可以从数据库中查询用户名和密码。 准备工作 ①创建一个用户表建表语句 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 账号状态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用户表②引入MybatisPuls和mysql驱动的依赖 dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.4.3/version /dependency dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId /dependency③配置数据库信息 spring:datasource:url: jdbc:mysql://localhost:3306/dy_security?characterEncodingutf-8serverTimezoneUTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver④定义Mapper接口 package com.dy.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.dy.domain.User;public interface UserMapper extends BaseMapperUser { }⑤修改Mapper扫描 package com.dy;import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication MapperScan(com.dy.mapper) public class SecurityApplication {public static void main(String[] args) {SpringApplication.run(SecurityApplication.class,args);} }⑥添加junit依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactId /dependency⑦测试MP是否能正常使用 SpringBootTest public class MapperTest {Autowiredprivate UserMapper userMapper;Testpublic void testUserMapper(){ListUser users userMapper.selectList(null);System.out.println(users);} }核心代码实现 创建一个类实现UserDetailsService接口重写其中的方法。更加用户名从数据库中查询用户信息 public class UserDetailsServiceImpl implements UserDetailsService {Autowiredprivate UserMapper userMapper;Overridepublic UserDetails loadUserByUsername(String username) throwsUsernameNotFoundException {//根据用户名查询用户信息LambdaQueryWrapperUser wrapper new LambdaQueryWrapper();wrapper.eq(User::getUserName,username);User user userMapper.selectOne(wrapper);//如果查询不到数据就通过抛出异常来给出提示if(Objects.isNull(user)){throw new RuntimeException(用户名或密码错误);}//TODO 根据用户查询权限信息 添加到LoginUser中//封装成UserDetails对象返回return new LoginUser(user);} }因为UserDetailsService方法的返回值是UserDetails类型所以需要定义一个类实现该接口把用户 信息封装在其中。 package com.dy.domain;import com.alibaba.fastjson.annotation.JSONField; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList; import java.util.Collection; import java.util.List;Data 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;} }注意如果要测试需要往用户表中写入用户数据并且如果你想让用户的密码是明文存储需要在密 码前加{noop}。例如 这样登陆的时候就可以用sg作为用户名1234作为密码来登陆了。 3.3.2密码加密存储 实际项目中我们不会把密码明文存储在数据库中。 默认使用的PasswordEncoder要求数据库中的密码格式为{id}password 。它会根据id去判断密码的 加密方式。但是我们一般不会采用这种方式。所以就需要替换PasswordEncoder。 我们一般使用SpringSecurity为我们提供的BCryptPasswordEncoder。 我们只需要使用把BCryptPasswordEncoder对象注入Spring容器中SpringSecurity就会使用该 PasswordEncoder来进行密码校验。 我们可以定义一个SpringSecurity的配置类SpringSecurity要求这个配置类要继承 WebSecurityConfigurerAdapter。 Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();} }3.3.3登录接口 接下我们需要自定义登陆接口然后让SpringSecurity对这个接口放行,让用户访问这个接口的时候不用 登录也能访问。 在接口中我们通过AuthenticationManager的authenticate方法来进行用户认证,所以需要在 SecurityConfig中配置把AuthenticationManager注入容器。 认证成功的话要生成一个jwt放入响应中返回。并且为了让用户下回请求时能通过jwt识别出具体的是哪个用户我们需要把用户信息存入redis可以把用户id作为key。 RestController public class LoginController {Autowiredprivate LoginServcie loginServcie;PostMapping(/user/login)public ResponseResult login(RequestBody User user){return loginServcie.login(user);} }Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {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();}BeanOverridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();} }Service public class LoginServiceImpl implements LoginServcie {Autowiredprivate AuthenticationManager authenticationManager;Autowiredprivate RedisCache redisCache;Overridepublic ResponseResult login(User user) {UsernamePasswordAuthenticationToken authenticationToken newUsernamePasswordAuthenticationToken(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 jwt JwtUtil.createJWT(userId);//authenticate存入redisredisCache.setCacheObject(login:userId,loginUser);//把token响应给前端HashMapString,String map new HashMap();map.put(token,jwt);return new ResponseResult(200,登陆成功,map);} }3.3.4认证过滤器 我们需要自定义一个过滤器这个过滤器会去获取请求头中的token对token进行解析取出其中的 userid。 使用userid去redis中获取对应的LoginUser对象。 然后封装Authentication对象存入SecurityContextHolder package com.dy.filter;import com.dy.domain.LoginUser; import com.dy.utils.JwtUtil; import com.dy.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.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;}String userid;//解析tokentry {Claims claims JwtUtil.parseJWT(token);userid claims.getSubject();} catch (Exception e) {e.printStackTrace();throw new RuntimeException(token非法);}//从redis中获取用户信息String redisKey login: userid;LoginUser loginUser redisCache.getCacheObject(redisKey);if(Objects.isNull(loginUser)){throw new RuntimeException(用户未登录);}//存入SecurityContextHolder//获取权限信息封装到AuthenticationToken中UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authenticationToken);//放行filterChain.doFilter(request,response);} }Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}AutowiredJwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;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();//把token校验过滤器添加到过滤器链中http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);}BeanOverridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();} }3.3.5退出登录 我们只需要定义一个登陆接口然后获取SecurityContextHolder中的认证信息删除redis中对应的数 据即可 package com.dy.service.impl;import com.dy.domain.LoginUser; import com.dy.domain.ResponseResult; import com.dy.domain.User; import com.dy.service.LoginService; import com.dy.utils.JwtUtil; import com.dy.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.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service;import java.util.HashMap; import java.util.Map; import java.util.Objects;Service public class LoginServiceImpl implements LoginService {Autowiredprivate AuthenticationManager authenticationManager;Autowiredprivate RedisCache redisCache;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生成一个jwt,jwt存入ResponseResult返回LoginUser loginUser (LoginUser) authenticate.getPrincipal();String userid loginUser.getUser().getId().toString();String jwt JwtUtil.createJWT(userid);MapString,String map new HashMap();map.put(token,jwt);redisCache.setCacheObject(login:userid,loginUser);//把完整的用户信息存入redis,userid作为keyreturn new ResponseResult(200,登录成功,map);}Overridepublic ResponseResult logout() {//获取SecurityContextHolder中的用户idUsernamePasswordAuthenticationToken authentication (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();LoginUser loginUser (LoginUser) authentication.getPrincipal();Long userid loginUser.getUser().getId();//删除redis中的idredisCache.deleteObject(login:userid);return new ResponseResult(200,注销成功);} }授权 1.权限系统的作用 例如一个学校图书馆的管理系统如果是普通学生登录就能看到借书还书相关的功能不可能让他看到 并且去使用添加书籍信息删除书籍信息等功能。但是如果是一个图书馆管理员的账号登录了应该就 能看到并使用添加书籍信息删除书籍信息等功能。 总结起来就是不同的用户可以使用不同的功能。这就是权限系统要去实现的效果。 我们不能只依赖前端去判断用户的权限来选择显示哪些菜单哪些按钮。因为如果只是这样如果有人知 道了对应功能的接口地址就可以不通过前端直接去发送请求来实现相关功能操作。 所以我们还需要在后台进行用户权限的判断判断当前用户是否有相应的权限必须具有所需权限才能 进行相应的操作。 2.授权基本流程 在SpringSecurity中会使用默认的FilterSecurityInterceptor来进行权限校验。在 FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication然后获取其中的 权限信息。当前用户是否拥有访问当前资源所需的权限。 所以我们在项目中只需要把当前登录用户的权限信息也存入Authentication。 然后设置我们的资源所需要的权限即可。 3.授权实现 3.1限制访问资源所需权限 SpringSecurity为我们提供了基于注解的权限控制方案这也是我们项目中主要采用的方式。我们可以 使用注解去指定访问对应的资源所需的权限。 但是要使用它我们需要先开启相关配置。 EnableGlobalMethodSecurity(prePostEnabled true)然后就可以使用对应的注解。PreAuthorize RestController public class HelloController {RequestMapping(/hello)PreAuthorize(hasAuthority(test))public String hello(){return hello;} }3.2封装权限信息 我们前面在写UserDetailsServiceImpl的时候说过在查询出用户后还要获取对应的权限信息封装到 UserDetails中返回。 先直接把权限信息写死封装到UserDetails中进行测试。 之前定义了UserDetails的实现类LoginUser想要让其能封装权限信息就要对其进行修改。 package com.dy.domain;import com.alibaba.fastjson.annotation.JSONField; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList; import java.util.Collection; import java.util.List;Data NoArgsConstructor public class LoginUser implements UserDetails {private User user;private ListString permissions;public LoginUser(User user, ListString permissions) {this.user user;this.permissions permissions;}JSONField(serialize false)private ListSimpleGrantedAuthority authorities;Overridepublic Collection? extends GrantedAuthority getAuthorities() {//把permission中String类型的权限信息封装成SimpleGrantedAuthority对象authorities new ArrayList();for(String permission:permissions){SimpleGrantedAuthority authority new SimpleGrantedAuthority(permission);authorities.add(authority);}return authorities;}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;} }LoginUser修改完后我们就可以在UserDetailsServiceImpl中去把权限信息封装到LoginUser中了。我们 写死权限进行测试后面我们再从数据库中查询权限信息。 package com.dy.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.dy.domain.LoginUser; import com.dy.domain.User; import com.dy.mapper.MenuMapper; import com.dy.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.lang.reflect.Member; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects;Service public class UserDetailsServiceImpl implements UserDetailsService {Autowiredprivate UserMapper userMapper;Overridepublic UserDetails loadUserByUsername(String username) throwsUsernameNotFoundException {LambdaQueryWrapperUser wrapper new LambdaQueryWrapper();wrapper.eq(User::getUserName,username);User user userMapper.selectOne(wrapper);if(Objects.isNull(user)){throw new RuntimeException(用户名或密码错误);}//TODO 根据用户查询权限信息 添加到LoginUser中ListString list new ArrayList(Arrays.asList(test));return new LoginUser(user,list);} }3.3从数据库查询权限信息 3.3.1RBAC权限模型 RBAC权限模型Role-Based Access Control即基于角色的权限控制。这是目前最常被开发者使用 也是相对易用、通用权限模型。 准备工作 USE sg_security; /*Table structure for table sys_menu */ DROP TABLE IF EXISTS sys_menu; CREATE TABLE sys_menu (id bigint(20) NOT NULL AUTO_INCREMENT,menu_name varchar(64) NOT NULL DEFAULT NULL COMMENT 菜单名,path varchar(200) DEFAULT NULL COMMENT 路由地址,component varchar(255) DEFAULT NULL COMMENT 组件路径,visible char(1) DEFAULT 0 COMMENT 菜单状态0显示 1隐藏,status char(1) DEFAULT 0 COMMENT 菜单状态0正常 1停用,perms varchar(100) DEFAULT NULL COMMENT 权限标识,icon varchar(100) DEFAULT # COMMENT 菜单图标,create_by bigint(20) DEFAULT NULL,create_time datetime DEFAULT NULL,update_by bigint(20) DEFAULT NULL,update_time datetime DEFAULT NULL,del_flag int(11) DEFAULT 0 COMMENT 是否删除0未删除 1已删除,remark varchar(500) DEFAULT NULL COMMENT 备注,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT2 DEFAULT CHARSETutf8mb4 COMMENT菜单表; /*Table structure for table sys_role */ DROP TABLE IF EXISTS sys_role; CREATE TABLE sys_role (id bigint(20) NOT NULL AUTO_INCREMENT,name varchar(128) DEFAULT NULL,role_key varchar(100) DEFAULT NULL COMMENT 角色权限字符串,status char(1) DEFAULT 0 COMMENT 角色状态0正常 1停用,del_flag int(1) DEFAULT 0 COMMENT del_flag,create_by bigint(200) DEFAULT NULL,create_time datetime DEFAULT NULL,update_by bigint(200) DEFAULT NULL,update_time datetime DEFAULT NULL,remark varchar(500) DEFAULT NULL COMMENT 备注,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT3 DEFAULT CHARSETutf8mb4 COMMENT角色表; /*Table structure for table sys_role_menu */ DROP TABLE IF EXISTS sys_role_menu; CREATE TABLE sys_role_menu (role_id bigint(200) NOT NULL AUTO_INCREMENT COMMENT 角色ID,menu_id bigint(200) NOT NULL DEFAULT 0 COMMENT 菜单id,PRIMARY KEY (role_id,menu_id) ) ENGINEInnoDB AUTO_INCREMENT2 DEFAULT CHARSETutf8mb4; /*Table structure for table sys_user */ DROP TABLE IF EXISTS sys_user; 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 账号状态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_INCREMENT3 DEFAULT CHARSETutf8mb4 COMMENT用户表; /*Table structure for table sys_user_role */ DROP TABLE IF EXISTS sys_user_role; CREATE TABLE sys_user_role (user_id bigint(200) NOT NULL AUTO_INCREMENT COMMENT 用户id,role_id bigint(200) NOT NULL DEFAULT 0 COMMENT 角色id,PRIMARY KEY (user_id,role_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;SELECTDISTINCT m.perms FROMsys_user_role urLEFT JOIN sys_role r ON ur.role_id r.idLEFT JOIN sys_role_menu rm ON ur.role_id rm.role_idLEFT JOIN sys_menu m ON m.id rm.menu_id WHEREuser_id 2AND r.status 0AND m.status 0实体类 package com.dy.domain;import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonInclude; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.io.Serializable; import java.util.Date;/*** 菜单表(Menu)实体类** author makejava* since 2021-11-24 15:30:08*/ TableName(value sys_menu) Data AllArgsConstructor NoArgsConstructor JsonInclude(JsonInclude.Include.NON_NULL) public class Menu implements Serializable {private static final long serialVersionUID -54979041104113736L;TableIdprivate Long id;/*** 菜单名*/private String menuName;/*** 路由地址*/private String path;/*** 组件路径*/private String component;/*** 菜单状态0显示 1隐藏*/private String visible;/*** 菜单状态0正常 1停用*/private String status;/*** 权限标识*/private String perms;/*** 菜单图标*/private String icon;private Long createBy;private Date createTime;private Long updateBy;private Date updateTime;/*** 是否删除0未删除 1已删除*/private Integer delFlag;/*** 备注*/private String remark; }3.3.2代码实现 我们只需要根据用户id去查询到其所对应的权限信息即可。 所以我们可以先定义个mapper其中提供一个方法可以根据userid查询权限信息。 package com.dy.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.dy.domain.Menu;import java.util.List;public interface MenuMapper extends BaseMapperMenu {ListString selectPermsByUserId(Long id); }尤其是自定义方法所以需要创建对应的mapper文件定义对应的sql语句 ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.dy.mapper.MenuMapperselect idselectPermsByUserId resultTypejava.lang.StringSELECTDISTINCT m.permsFROMsys_user_role urLEFT JOIN sys_role r ON ur.role_id r.idLEFT JOIN sys_role_menu rm ON ur.role_id rm.role_idLEFT JOIN sys_menu m ON m.id rm.menu_idWHEREuser_id #{id}AND r.status 0AND m.status 0/select /mapper在application.yml中配置mapperXML文件的位置 spring:datasource:url: jdbc:mysql://localhost:3306/dy_security?characterEncodingutf-8serverTimezoneUTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus:mapper-locations: classpath*:/mapper/**/*.xml然后我们可以在UserDetailsServiceImpl中去调用该mapper的方法查询权限信息封装到LoginUser对象 中即可。 package com.dy.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.dy.domain.LoginUser; import com.dy.domain.User; import com.dy.mapper.MenuMapper; import com.dy.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.lang.reflect.Member; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects;Service public class UserDetailsServiceImp implements UserDetailsService {Autowiredprivate UserMapper userMapper;Autowiredprivate MenuMapper menuMapper;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//查询用户信息LambdaQueryWrapperUser queryWrapper new LambdaQueryWrapper();queryWrapper.eq(User::getUserName,username);User user userMapper.selectOne(queryWrapper);//如果没有查询到用户就抛出异常if(Objects.isNull(user)){throw new RuntimeException(用户名或者密码错误);}//查询对应的权限信息 // ListString list new ArrayList(Arrays.asList(test,admin));ListString list menuMapper.selectPermsByUserId(user.getId());//把数据封装成UserDetailsreturn new LoginUser(user,list);} }自定义失败处理 我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json这样可以 让前端能对响应进行统一的处理。要实现这个功能我们需要知道SpringSecurity的异常处理机制。 在SpringSecurity中如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕 获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。 如果是认证过程中出现的异常会被封装成AuthenticationException然后调用 AuthenticationEntryPoint对象的方法去进行异常处理。 如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对 象的方法去进行异常处理。 所以如果我们需要自定义异常处理我们只需要自定义AuthenticationEntryPoint和 AccessDeniedHandler然后配置给SpringSecurity即可 ①自定义实现类 Component public class AccessDeniedHandlerImpl implements AccessDeniedHandler {Overridepublic void handle(HttpServletRequest request, HttpServletResponse response,AccessDeniedException accessDeniedException) throws IOException,ServletException {ResponseResult result new ResponseResult(HttpStatus.FORBIDDEN.value(),权限不足);String json JSON.toJSONString(result);WebUtils.renderString(response,json);} }Component public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {Overridepublic void commence(HttpServletRequest request, HttpServletResponseresponse, AuthenticationException authException) throws IOException,ServletException {ResponseResult result newResponseResult(HttpStatus.UNAUTHORIZED.value(), 认证失败请重新登录);String json JSON.toJSONString(result);WebUtils.renderString(response,json);} }②配置给SpringSecurity 先注入对应的处理器 Autowired private AuthenticationEntryPoint authenticationEntryPoint; Autowired private AccessDeniedHandler accessDeniedHandler;然后我们可以使用HttpSecurity对象的方法去配置。 http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);跨域 浏览器出于安全的考虑使用 XMLHttpRequest对象发起 HTTP请求时必须遵守同源策略否则就是跨 域的HTTP请求默认情况下是被禁止的。 同源策略要求源相同才能正常进行通信即协议、域名、端 口号都完全一致。 前后端分离项目前端项目和后端项目一般都不是同源的所以肯定会存在跨域请求的问题。 所以我们就要处理一下让前端能进行跨域请求。 ①先对SpringBoot配置运行跨域请求 Configuration public class CorsConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegistry registry) {// 设置允许跨域的路径registry.addMapping(/**)// 设置允许跨域请求的域名.allowedOriginPatterns(*)// 是否允许cookie.allowCredentials(true)// 设置允许的请求方式.allowedMethods(GET, POST, DELETE, PUT)// 设置允许的header属性.allowedHeaders(*)// 跨域允许时间.maxAge(3600);} }②开启SpringSecurity的跨域访问 由于我们的资源都会收到SpringSecurity的保护所以想要跨域访问还要让SpringSecurity运行跨域访 问。 Override protected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers(/user/login).anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();//添加过滤器http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);//配置异常处理器http.exceptionHandling()//配置认证失败处理器.authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);//允许跨域http.cors(); }其他权限校验方法 我们前面都是使用PreAuthorize注解然后在在其中使用的是hasAuthority方法进行校验。 SpringSecurity还为我们提供了其它方法例如hasAnyAuthorityhasRolehasAnyRole等。 这里我们先不急着去介绍这些方法我们先去理解hasAuthority的原理然后再去学习其他方法你就更 容易理解而不是死记硬背区别。并且我们也可以选择定义校验方法实现我们自己的校验逻辑。 hasAuthority方法实际是执行到了SecurityExpressionRoot的hasAuthority大家只要断点调试既可知 道它内部的校验原理。 它内部其实是调用authentication的getAuthorities方法获取用户的权限列表。然后判断我们存入的方法 参数数据在权限列表中。 hasAnyAuthority方法可以传入多个权限只有用户有其中任意一个权限都可以访问对应资源。 PreAuthorize(hasAnyAuthority(admin,test,system:dept:list)) public String hello(){return hello; }hasRole要求有对应的角色才可以访问但是它内部会把我们传入的参数拼接上 ROLE_ 后再去比较。所 以这种情况下要用用户对应的权限也要有 ROLE_ 这个前缀才可以 PreAuthorize(hasRole(system:dept:list)) public String hello(){return hello; }hasAnyRole 有任意的角色就可以访问。它内部也会把我们传入的参数拼接上 ROLE_ 后再去比较。所以 这种情况下要用用户对应的权限也要有 ROLE_ 这个前缀才可以。 PreAuthorize(hasAnyRole(admin,system:dept:list)) public String hello(){return hello; }自定义权限校验方法 我们也可以定义自己的权限校验方法在PreAuthorize注解中使用我们的方法。 Component(ex) public class SGExpressionRoot {public boolean hasAuthority(String authority){//获取当前用户的权限Authentication authentication SecurityContextHolder.getContext().getAuthentication();LoginUser loginUser (LoginUser) authentication.getPrincipal();ListString permissions loginUser.getPermissions();//判断用户权限集合中是否存在authorityreturn permissions.contains(authority);} }在SPEL表达式中使用 ex相当于获取容器中bean的名字未ex的对象。然后再调用这个对象的 hasAuthority方法 RequestMapping(/hello) PreAuthorize(ex.hasAuthority(system:dept:list)) public String hello(){return hello; }基于配置的权限控制 我们也可以在配置类中使用使用配置的方式对资源进行权限控制。 Override protected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers(/user/login).anonymous().antMatchers(/testCors).hasAuthority(system:dept:list222)// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();//添加过滤器http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);//配置异常处理器http.exceptionHandling()//配置认证失败处理器.authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);//允许跨域http.cors(); }CSRF CSRF是指跨站请求伪造Cross-site request forgery是web常见的攻击之一。 SpringSecurity去防止CSRF攻击的方式就是通过csrf_token。后端会生成一个csrf_token前端发起请 求的时候需要携带这个csrf_token,后端会有过滤器进行校验如果没有携带或者是伪造的就不允许访 问。 我们可以发现CSRF攻击依靠的是cookie中所携带的认证信息。但是在前后端分离的项目中我们的认证信 息其实是token而token并不是存储中cookie中并且需要前端代码去把token设置到请求头中才可 以所以CSRF攻击也就不用担心了。 认证成功处理器 实际上在UsernamePasswordAuthenticationFilter进行登录认证的时候如果登录成功了是会调用 AuthenticationSuccessHandler的方法进行认证成功后的处理的。AuthenticationSuccessHandler就是 登录成功处理器。 我们也可以自己去自定义成功处理器进行成功后的相应处理。 Component public class SGSuccessHandler implements AuthenticationSuccessHandler {Overridepublic void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response, Authentication authentication) throws IOException,ServletException {System.out.println(认证成功了);} }Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate AuthenticationSuccessHandler successHandler;Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().successHandler(successHandler);http.authorizeRequests().anyRequest().authenticated();} }认证失败处理器 实际上在UsernamePasswordAuthenticationFilter进行登录认证的时候如果认证失败了是会调用 AuthenticationFailureHandler的方法进行认证失败后的处理的。AuthenticationFailureHandler就是登 录失败处理器。 我们也可以自己去自定义失败处理器进行失败后的相应处理。 Component public class SGFailureHandler implements AuthenticationFailureHandler {Overridepublic void onAuthenticationFailure(HttpServletRequest request,HttpServletResponse response, AuthenticationException exception) throwsIOException, ServletException {System.out.println(认证失败了);} }Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate AuthenticationSuccessHandler successHandler;Autowiredprivate AuthenticationFailureHandler failureHandler;Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()// 配置认证成功处理器.successHandler(successHandler)// 配置认证失败处理器.failureHandler(failureHandler);http.authorizeRequests().anyRequest().authenticated();} }登出成功处理器 Component public class SGLogoutSuccessHandler implements LogoutSuccessHandler {Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponseresponse, Authentication authentication) throws IOException, ServletException {System.out.println(注销成功);} }Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate AuthenticationSuccessHandler successHandler;Autowiredprivate AuthenticationFailureHandler failureHandler;Autowiredprivate LogoutSuccessHandler logoutSuccessHandler;Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()// 配置认证成功处理器.successHandler(successHandler)// 配置认证失败处理器.failureHandler(failureHandler);http.logout()//配置注销成功处理器.logoutSuccessHandler(logoutSuccessHandler);http.authorizeRequests().anyRequest().authenticated();} }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/927657.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站建设维护教程网站开发承诺函

转载于:https://www.cnblogs.com/mountian-lion/p/6353819.html

金融网站建设成功案例做网站具体流程

一、cluster ip Cluster IP 是 Kubernetes 中 Service 的 IP 地址,它是一个虚拟 IP 地址,用于集群内的 Pod 相互通信。 例如: Cluster IP:2.2.2.2负载的真实Pod IP:1.1.1.1 场景: Pod A 的 IP 地址是 …

PSRAM 是什么

SRAM 的英文是 Pseudo Static Random-Access Memory。 可以拆解为:Pseudo - 前缀,意为“伪”、“假的”Static - 静态的Random-Access Memory - 随机存取存储器所以,它的全称直译就是“伪静态随机存取存储器”。 在…

怎么自己改自己做的网站的图片wordpress js 钩子

近几年来,新能源汽车发展越来越快,而限制新能源电动汽车发展的主要因素是续航里程和充电问题。续航里程要靠提高电池性能来解决,而解决充电问题就要靠充电桩的普及来实现。下面小编带着大家一起来了解一下直流充电桩和交流充电桩给电动汽车充…

蓝色大气网站源码短视频素材库大全

流量控制的功能就是让发送方的发送速率不要太快,以便让接收方来的及接受,因此可以说流量控制是一个速度匹配服务,匹配发送方的发送速率和接收方的读取速率。 TCP利用滑动窗口机制来实现流量控制,滑动窗口的基本原理是&#xff0c…

Debian 13 eza 安装与常用参数

以下参数按「使用频率」与「实用场景」分组,可直接当作速查表。所有示例均亲测可 copy-paste。一、10 条「每天必用」参数目的 命令示例 说明1. 彩色+图标基础列目录 eza --icons=auto 需 Nerd Font;无则自动 fallba…

Syncthing 2.0 版本开机自启

2.0 版本不再支持单-,需要双-- 🛠️ 命令行选项优化 ​现代化了命令行选项解析方式。​​ 旧的短横线长选项(如 -home)​不再支持,必须使用 ​双横线,如 --home。 所以注意要 --no-console --no-browser方法在 …

鲜花 10.4:【半 whk 向】临项交换法贪心

题源:青岛 58 中高一作业。新定义能这么出? 直接考虑(3),这是一个经典问题 [NOIP 2012 提高组] 国王游戏 的模型,即临项交换法贪心。 题意即重排一个给定的二元组序列,使得 \(\max_{i=1}^n f_i\) 最小,其中, …

商务网站建设与维护 试题网站空间商盗取数据

随着微信小程序的日益普及,越来越多的人开始关注如何利用小程序来提供便捷的服务。对于心理咨询行业来说,搭建一个心理咨询预约小程序可以大大提高服务的效率和用户体验。本文以乔拓云平台为例,详细介绍如何轻松搭建一个心理咨询预约小程序。…

详细介绍:CompLLM 来了:长文本 QA 效率革命,线性复杂度 + 缓存复用,推理速度与效果双丰收

详细介绍:CompLLM 来了:长文本 Q&A 效率革命,线性复杂度 + 缓存复用,推理速度与效果双丰收2025-10-04 22:43 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !…

服务好的南昌网站设计好看欧美视频网站模板下载 迅雷下载地址

冒泡排序源代码,带有详细注释,希望可以帮助到大家,有用的话点个赞和收藏吧,感谢大家的阅读,Java小白一直在努力,承蒙大神们的厚爱。 定义 在程序设计语言中,排序算法主要有冒泡排序、快速排序、…

前端学习教程-Pinia 教程

Pinia 是 Vue 官方推荐的状态管理库,替代了传统的 Vuex,专为 Vue3 设计,支持 Composition API 和 TypeScript,API 更简洁,是目前 Vue 项目状态管理的最佳选择。 一、为什么选择 Pinia? 与 Vuex 相比,Pinia 具有…

dede世界杯网站模板中国室内设计网官网总裁

简介: Stunnel是一个用于创建SSL加密隧道的工具,针对本身无法进行TLS或SSL通信的客户端及服务器,Stunnel 可提供安全的加密连接。可以用于保护服务器之间的通信。您可以在每台服务器上安装Stunnel,并将其配置为在公网上加密传输数…

基于pycharm实现html文件的快速达成问题讨论

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

一篇计算机类的论文的结构/架构是怎么样的?

一篇计算机类的论文的结构/架构是怎么样的?本博客是博主个人学习时的一些记录,不保证是为原创,个别文章加入了转载的源地址,还有个别文章是汇总网上多份资料所成,在这之中也必有疏漏未加标注处,如有侵权请与博主…

大模型, 多少b 占用多少显存

大模型, 多少b 占用多少显存可以看出来,1B=109byte≈1GB 1 𝐵 10 9 𝑏 𝑦 𝑡 𝑒 ≈ 1 𝐺 𝐵 ,1B和1G的大小基本一致,所以我们记作B和G相等。但是,1B模型参数对应多少G内存和参数的精度有关。如果是全…

购物网站名字大全单页网站模板安装

把数据库的备份文件放到服务器的任意目录下先, 然后按下面的步骤做。 如何从备份设备还原备份(企业管理器) 从备份设备还原备份 展开服务器组,然后展开服务器。 展开"数据库"文件夹,右击数据库,指…

布谷娱乐直播架构源码开发实用功能:技术驱动更迭的创新体验

布谷娱乐直播架构源码开发实用功能:技术驱动更迭的创新体验pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Conso…

摄影后期教程网站个人网站 百度推广

富格林悉知,现货黄金由于活跃的行情给投资者带来不少的盈利的机会,吸引着众多的投资者进场做单。但在黄金投资市场中一定要掌握可信的投资方法,提前布局好策略,这样才能增加安全获利的机会。不建议直接进入市场做单,因…