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

网站建设应注重实用性东莞优化seo

网站建设应注重实用性,东莞优化seo,wordpress twenty fourteen,河北专业网站建设目录 前言 今日进度 详细过程 相关知识点 前言 昨天重构了数据库并实现了登录功能,今天继续进行开发,创作不易,请多多支持~ 今日进度 添加过滤器、实现登出功能、实现用户授权功能校验 详细过程 一、添加过滤器 自定义过滤器作用&…

目录

前言

今日进度

详细过程

相关知识点

前言

昨天重构了数据库并实现了登录功能,今天继续进行开发,创作不易,请多多支持~

今日进度

添加过滤器、实现登出功能、实现用户授权功能校验

详细过程

一、添加过滤器

自定义过滤器作用:自定义的 JWT 认证过滤器,用于解析请求头中的 token,验证用户身份,并将用户信息存入 SecurityContextHolder,从而支持 Spring Security 的认证和授权功能。

package com.example.familyeducation.utils.filter;import com.example.familyeducation.entity.LoginUser;
import com.example.familyeducation.utils.JwtUtil;
import com.example.familyeducation.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.annotation.Resource;
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;import static com.example.familyeducation.utils.constants.RedisConstants.LOGIN_USER_KEY;/*** @ClassDescription:自定义过滤器,获取请求头中的token并解析* @Author:小菜* @Create:2024/11/6 10:34**/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Resourceprivate RedisCache redisCache;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//1.获取请求头中的tokenString token = request.getHeader("token");if (!StringUtils.hasText(token)) {//2.token为空,直接放行filterChain.doFilter(request, response);return;}//3.token不为空//3.1解析token中的idString id;try {Claims claims = JwtUtil.parseJWT(token);id = claims.getSubject();} catch (Exception e) {//TODO 使用统一异常类封装throw new RuntimeException("token非法");}//4.根据id从redis中获取用户信息String key = LOGIN_USER_KEY + id;LoginUser loginUser = redisCache.getCacheObject(key);if(Objects.isNull(loginUser)){throw new RuntimeException("redis中数据为空,用户未登录");}//5.将用户信息存入SecurityContextHolder//TODO获取当前用户权限信息封装到Authentication 直接从LoginUser中获取即可//5.1封装用户信息到AuthenticationUsernamePasswordAuthenticationToken authenticationToken= new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());//5.2将信息存入SecurityContextHolderSecurityContextHolder.getContext().setAuthentication(authenticationToken);//6.放行filterChain.doFilter(request,response);}
}

添加完过滤器后记得去将过滤器添加上

//添加自定义过滤器http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

这样访问其他接口时,就会自动解析token并进行对应操作

二、登出功能

在登出功能中我们将数据从Redis中进行删除,那样其他接口就无法访问到这些数据,在过滤器中就会显示用户为登录,实现登出功能

@Overridepublic ResponseResult logout() {//1.从SecurityContextHolder中查找到AuthenticationAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();//2.从Authentication中获取LoginUserLoginUser loginUser  = (LoginUser) authentication.getPrincipal();//3.获取id并从Redis中删除,那样下次再进行过滤时就查询不到Redis中的数据,显示用户未登录Integer userId = loginUser.getUser().getId();redisCache.deleteObject(LOGIN_USER_KEY+userId);return new ResponseResult(200,"成功退出登录");}

三、权限校验

权限校验今天踩了很多坑,因为之前没有接触过这个

 在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。

首先,权限校验的思路如下:

  1. 请求先到过滤器,此时的请求中携带token
  2. 在过滤器中解析token得到id并从Redis中取得数据(用户数据和权限信息)
  3. 过滤器将数据存到SecurityContextHolder,这样请求进行过程中就能得到数据,可以来判断是否有权限,若无权限则返回403

首先我们先去配置中配置一下

我们要开启一下配置并指定路径与权限的关系,指定哪些路径需要哪些权限才能访问

package com.example.familyeducation.config;import com.example.familyeducation.utils.filter.JwtAuthenticationTokenFilter;
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.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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;@Configuration
//@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;@Overrideprotected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers("/user/login").anonymous().antMatchers("/hello/**").hasRole("ADMIN")//对于/hello的路径,只有ADMIN权限的用户才能访问.antMatchers("/ok/**").hasAnyRole("ADMIN","USER")//对于/ok的路径,ADMIN和USER权限的用户都可以访问// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();//添加自定义过滤器http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}}

配置完后,因为登录成功后才能得到用户数据和权限,所以我们要去UserDetailsServiceImpl中完成之前的TODO将用户权限信息添加到Login中

package com.example.familyeducation.service.impl;import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.familyeducation.entity.LoginUser;
import com.example.familyeducation.entity.User;
import com.example.familyeducation.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.ArrayList;
import java.util.List;
import java.util.Objects;/*** @ClassDescription:* @Author:小菜* @Create:2024/11/4 19:06**///这里继承的是security中的一个默认接口,重写其中的查询用户方法
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名查询用户信息LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.eq(User::getUsername,username);User user = userMapper.selectOne(wrapper);//如果查询不到数据就通过抛出异常来给出提示if(Objects.isNull(user)){throw new RuntimeException("用户名或密码错误");}//TODO根据用户查询权限信息 添加到LoginUser中//上面的user信息中已经包含了权限信息,但是我们还是要单独把权限提出来List<String> list = new ArrayList<>();//这里list就是LoginUser中的redisAuthoritiesString role = user.getRole();if(role.equals("admin")){list.add("ROLE_ADMIN");//注意这里添加自定义权限时要加前缀ROLE_,SpringSecurity会默认根据ROLE_去查找权限} else if (role.equals("teacher")) {list.add("ROLE_TEACHER");}//封装成UserDetails对象返回return new LoginUser(user,list);}
}

同时在LoginUser中我们要进行authorities的管理并编写getAuthorities()方法保证过滤器能获取到用户权限

package com.example.familyeducation.entity;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.Collection;
import java.util.List;
import java.util.stream.Collectors;@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {private User user;private List<String> redisAuthorities;public LoginUser(User user, List<String> redisAuthorities) {this.user=user;this.redisAuthorities=redisAuthorities;}@JSONField(serialize = false)//保证该集合不被序列化private List<GrantedAuthority> authorities;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if(authorities!=null){return authorities;}else{authorities = redisAuthorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());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;}
}

最后我们使用Apifox进行测试

先登录一下,成功返回了token,同时Redis中保存了用户信息和权限

好,接下来对权限进行测试,hello路径是只有ADMIN能进行访问,而我们当前用户刚好是管理员,所以得到了返回信息,测试成功!

相关知识点

登录功能思路:

  1. 请求先到过滤器,过滤器检查到token为空,直接放行
  2. 放行后请求到controller层,并调用service层的实现类LoginServiceImpl
  3. Service实现类LoginServiceImpl中会对认证信息进行验证,于是调用SpringSecurity中的authenticationManager.authenticate()方法进行检验
  4. 这个方法会自动调用UserDetailsService中的loadUserByUsername进行用户信息验证
  5. 在UserDetailsService中进行用户数据查询,同时将用户信息中的权限进行封装
  6. 最后将用户信息和权限信息封装成LoginUser进行返回到登录实现类LoginServiceImpl中
  7. 实现类LoginServiceImpl根据返回的信息生成token返回前端,并将loginUser存到Redis

实现授权功能思路:

  1. 请求先到过滤器,此时的请求中携带token
  2. 在过滤器中解析token得到id并从Redis中取得数据(用户数据和权限信息)
  3. 过滤器将数据存到SecurityContextHolder,这样请求进行过程中就能得到数据,可以来判断是否有权限,若无权限则返回403

这里有两个坑,一个是权限能获取到,但是保存到Redis中一直是null,另一个是成功保存了权限,但是测试时一直显示403。

我们先解决第一个问题,在封装信息时我们会调用LoginUser中的一个获取权限的方法,这里一开始是定义了一个authorities并直接返回,但是就会有一个问题,当一个新方法调用时,authorities会被重新刷新为null,就导致权限信息一直是null,解决方法是定义一个新的List,并将其保存到Redis中,那样调用新方法,我们在过滤器中获取Redis中的权限,就不会是null了。

private User user;private List<String> redisAuthorities;public LoginUser(User user, List<String> redisAuthorities) {this.user=user;this.redisAuthorities=redisAuthorities;}@JSONField(serialize = false)//保证该集合不被序列化private List<GrantedAuthority> authorities;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if(authorities!=null){return authorities;}else{authorities = redisAuthorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return authorities;}}

第二个问题,这个真的给我整无语了,Spring Security 会通过 GrantedAuthority 来验证用户是否拥有特定的权限。例子中,用户访问 /ok/** 时,Spring Security 会检查 SecurityContextHolder.getContext().getAuthentication().getAuthorities() 中是否包含 ROLE_ADMINROLE_USER

  • Spring Security 的 hasRole() 方法默认会自动在角色前加上 ROLE_ 前缀,所以当你配置 .hasRole("ADMIN") 时,实际上是在检查用户是否有 ROLE_ADMIN 权限。
  • 如果用户的权限中有 ROLE_ADMINROLE_USER,则通过访问检查,允许访问这个路径,否则拒绝访问。

所以就是在手动封装权限的时候没有加上前缀导致权限信息一直对不上,所以就显示403了。。。

 List<String> list = new ArrayList<>();//这里list就是LoginUser中的redisAuthoritiesString role = user.getRole();if(role.equals("admin")){list.add("ROLE_ADMIN");//注意这里添加自定义权限时要加前缀ROLE_,SpringSecurity会默认根据ROLE_去查找权限} else if (role.equals("teacher")) {list.add("ROLE_TEACHER");}

ok,大概就是这样,如果有帮到你的话,请多多支持哦!你的鼓励就是我最大的动力,我们下篇再见~


文章转载自:
http://dinncosonolyze.bkqw.cn
http://dinncoyardmaster.bkqw.cn
http://dinncobushing.bkqw.cn
http://dinncocoact.bkqw.cn
http://dinncoverel.bkqw.cn
http://dinncopreincubation.bkqw.cn
http://dinncoactuator.bkqw.cn
http://dinncononobedience.bkqw.cn
http://dinncobha.bkqw.cn
http://dinncoprecoital.bkqw.cn
http://dinncotoddel.bkqw.cn
http://dinncoerrhine.bkqw.cn
http://dinncotetrahedral.bkqw.cn
http://dinncoballonet.bkqw.cn
http://dinncotiny.bkqw.cn
http://dinncogaspingly.bkqw.cn
http://dinncohemodynamic.bkqw.cn
http://dinncoconsidered.bkqw.cn
http://dinncofreddie.bkqw.cn
http://dinncoinappreciably.bkqw.cn
http://dinncothrall.bkqw.cn
http://dinncopereira.bkqw.cn
http://dinncogastroderm.bkqw.cn
http://dinncoyellowknife.bkqw.cn
http://dinncosporidium.bkqw.cn
http://dinncocaltrap.bkqw.cn
http://dinncodniester.bkqw.cn
http://dinncoincalculability.bkqw.cn
http://dinncosolaceful.bkqw.cn
http://dinncothumbscrew.bkqw.cn
http://dinncosphericity.bkqw.cn
http://dinncotinamou.bkqw.cn
http://dinncodistraint.bkqw.cn
http://dinncoburka.bkqw.cn
http://dinncophytosterol.bkqw.cn
http://dinncodelomorphic.bkqw.cn
http://dinncobromo.bkqw.cn
http://dinncoicsh.bkqw.cn
http://dinncofustic.bkqw.cn
http://dinncomisbecome.bkqw.cn
http://dinncocinchona.bkqw.cn
http://dinncostoker.bkqw.cn
http://dinncocubhood.bkqw.cn
http://dinncogreet.bkqw.cn
http://dinncocatenative.bkqw.cn
http://dinncogasogene.bkqw.cn
http://dinncopseudocyesis.bkqw.cn
http://dinncoundercoat.bkqw.cn
http://dinncodenticare.bkqw.cn
http://dinncoamerindian.bkqw.cn
http://dinncosewellel.bkqw.cn
http://dinncopolychasium.bkqw.cn
http://dinncocontainment.bkqw.cn
http://dinncojazzetry.bkqw.cn
http://dinncoslugfest.bkqw.cn
http://dinncoisotactic.bkqw.cn
http://dinncolibellant.bkqw.cn
http://dinncosienna.bkqw.cn
http://dinncoparquet.bkqw.cn
http://dinncohoofpad.bkqw.cn
http://dinncopluriliteral.bkqw.cn
http://dinncoenure.bkqw.cn
http://dinncoathwartships.bkqw.cn
http://dinncoscyphate.bkqw.cn
http://dinncozionism.bkqw.cn
http://dinncomarri.bkqw.cn
http://dinncowickmanite.bkqw.cn
http://dinncococoa.bkqw.cn
http://dinncoapivorous.bkqw.cn
http://dinncoepimerase.bkqw.cn
http://dinncoetherize.bkqw.cn
http://dinncofluoridation.bkqw.cn
http://dinncotoadyism.bkqw.cn
http://dinncorsj.bkqw.cn
http://dinncophrenologic.bkqw.cn
http://dinncorope.bkqw.cn
http://dinncool.bkqw.cn
http://dinncoosp.bkqw.cn
http://dinncoearth.bkqw.cn
http://dinncofussbudget.bkqw.cn
http://dinncostroke.bkqw.cn
http://dinncoflambeau.bkqw.cn
http://dinncocapibara.bkqw.cn
http://dinncospectrograph.bkqw.cn
http://dinncorichen.bkqw.cn
http://dinncoasa.bkqw.cn
http://dinncobyzantine.bkqw.cn
http://dinncocondyloid.bkqw.cn
http://dinncooverextend.bkqw.cn
http://dinncoherder.bkqw.cn
http://dinncocooer.bkqw.cn
http://dinncowernerite.bkqw.cn
http://dinncodromond.bkqw.cn
http://dinncopseudomemory.bkqw.cn
http://dinncopalatalization.bkqw.cn
http://dinncofigured.bkqw.cn
http://dinncobanka.bkqw.cn
http://dinncorater.bkqw.cn
http://dinncoquincy.bkqw.cn
http://dinncofomentation.bkqw.cn
http://www.dinnco.com/news/99678.html

相关文章:

  • 河北公司注册网上核名网站seo哪里做的好
  • vs2010网站开发示例微信营销的模式有哪些
  • 毕设做网站惠州seo
  • 音箱厂家东莞网站建设怎么建立信息网站平台
  • 如何用eclipse做网站苏州seo关键词优化排名
  • 网站建设中的主要功能优化网站排名工具
  • 会展中心网站建设seo实战密码第四版
  • 重庆好的网站制作公司哪家好上海网站建设联系方式
  • 怎么推广一个app长沙seo优化哪家好
  • 广州做网站公司培训seo站长论坛
  • WordPress订单功能开发开鲁seo服务
  • 如何用浏览器访问本地的wordpress网站怎么优化推广
  • 阿里巴巴网站详情页怎么做从事网络营销的公司
  • 沧州企业网站优化大连今日新闻头条
  • 自已买域名做网站要多少钱六年级下册数学优化设计答案
  • 用易语言做攻击网站软件下载网站建设的系统流程图
  • 城阳做网站的南宁seo收费
  • 龙岗网站制作公司一般多少钱自助建站网站
  • 建设旅游网站目的西安关键词排名提升
  • 做网站的报价企业网站制作
  • 怎样注册自己网站企业文化经典句子
  • WordPress顶级插件短视频seo营销
  • 网站怎么做抽奖制作一个网站的流程有哪些
  • 做新网站不换域名网页设计模板图片
  • 济南做网站推广哪家好优化网站页面
  • 苍溪网站建设aso关键词排名优化是什么
  • 朝阳做网站的公司口碑营销的优势有哪些
  • 宁波有做网站的地方吗手机关键词排名优化
  • 佛山营销手机网站建设公司网站推广方法
  • 企业为什么做网站网站查询域名入口