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

大丰区住房和城乡建设局网站怎么样做免费的百度seo

大丰区住房和城乡建设局网站,怎么样做免费的百度seo,广东网站建设效果,做外贸网站的价格1. 整体概述 权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制到资源,用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。 1.1 认证概述 认证是确认用户身份的过程,确保用户是谁。 1.1.1 …

1. 整体概述

        权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制到资源,用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。

1.1 认证概述

 
认证是确认用户身份的过程,确保用户是谁。

1.1.1 认证组件

Spring Security的认证过程涉及以下组件

AuthenticationManager:作为认证的核心,AuthenticationManager负责处理用户的认证请求。它会委托不同的认证提供者(例如DaoAuthenticationProvider)来验证用户身份。AuthenticationManager接收一个包含用户凭证(如用户名、密码)的Authentication对象,然后通过认证提供者验证这些凭证的正确性。如果验证通过,AuthenticationManager会返回一个包含认证信息的Authentication对象。

Authentication:Authentication表示用户的认证信息,通常包括用户名、密码以及认证后得到的角色和权限。它是Spring Security中用户身份验证的核心数据结构。Authentication接口通常由UsernamePasswordAuthenticationToken等实现类来表示。认证完成后,Authentication对象将包含用户的认证状态和权限信息。

SecurityContextHolder:SecurityContextHolder是Spring Security中的核心类之一,它用来存储当前用户的认证信息。认证成功后,Authentication对象会被保存在SecurityContext中,SecurityContext会由SecurityContextHolder管理。在每个请求周期内,SecurityContextHolder提供对当前用户认证信息的访问,使得后续的请求可以通过SecurityContextHolder.getContext().getAuthentication()来获取当前用户的身份信息。

 1.1.2 认证过滤器链

1.1.3 认证流程步骤

1.2 授权概述


授权是根据用户身份信息判断用户是否有权限访问某些资源的过程。Spring Security的授权过程涉及以下组件:

AccessDecisionManager:AccessDecisionManager负责根据用户的认证信息和请求的资源,做出是否允许访问的决策。它会根据配置的权限要求以及用户的角色信息,决定用户是否能够访问特定的资源。AccessDecisionManager会使用多个AccessDecisionVoter来进行投票,综合多个投票结果后,做出最终的访问决策。

AccessDecisionVoter:AccessDecisionVoter是负责对用户访问权限进行投票的组件。它根据用户的Authentication对象和访问资源的ConfigAttribute(例如角色或权限要求)进行匹配。每个AccessDecisionVoter会判断用户是否符合特定的访问要求,并给出投票结果。所有投票的结果会由AccessDecisionManager汇总,最终决定是否允许访问。

ConfigAttribute:ConfigAttribute用于描述受保护资源的权限要求。它定义了访问某个资源所需的权限(如角色或操作权限)。ConfigAttribute通常与AccessDecisionManager结合使用,它告知AccessDecisionVoter该资源需要哪些权限,AccessDecisionVoter则根据这些要求判断用户是否具备访问该资源的权限。

1.3. 引入依赖

        一旦引入Spring security依赖后,系统中所有的资源都受保护起来,必须进行认证之后才能够访问,没有认证直接访问资源时,会跳转到Spring security默认的登录页面:http://localhost:8080/login

<!--Spring security -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

        Spring security会默认提供一个用户和密码,用户是user,密码是启动时的一个字符串:

 如果觉得启动时生成的默认密码比较长不方便登录,也可以使用配置文件配置固定的用户名和密码,密码前缀{noop}表示密码是明文。

2. 实现思路

        整个基于security框架实现认证授权思路如上所示,绿色框是框架已经实现的内容,白色框需要用户改写和实现的内容。 

2.1 认证登陆

        完成上图中白色框中的内容,具体步骤如下所示。

2.1.1 查询数据库用户

步骤1: 通过登录用户名查数据库中用户信息

通过自定义UserDetailsService,改写里面的loadUserByUsername方法,利用mybatis或其他框架从数据库中查询出用户信息。

除此之外,改写UserDetails接口中的方法,把数据库中查出的用户信息封装成UserDetails进行返回。

@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//查询用户信息LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUserAccount,username);User user = userMapper.selectOne(queryWrapper);if(user==null){throw  new RuntimeException("此用户"+username+" 不存在");}//TODO 查询对应的权限信息// 把数据封装成UserDetailsreturn new LoginUser(user);}
}
@Data // get set 方法
@NoArgsConstructor // 空参构造器
@AllArgsConstructor // 全参构造器
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.getUserAccount();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

 完成以上代码后,在数据库中存入用户,打开security默认登录页面测试是否能够使用数据库中存入的用户进行页面登录。

注意:因为目前还未做用户密码加密,密码是按明文存储的,所以在密码前需要加上{noop}标识

弹出以上界面说明登录成功,第一步操作完成。 

2.1.2 密码加密功能

步骤2: 用户密码加密功能

增加security配置,指定加密方式,从而自动实现用户密码按加密方式匹配,并要求用户新增接口和修改接口中,密码字段要调用配置中的加密方式进行明文加密,然后再存储数据库中。

增加security配置,定义密码加密方式。

@Configuration
@EnableWebSecurity
public class SecurityConfig  {// 配置密码加密方式,全局自动按这个方式加密@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

增加此配置后,所有密码均采用此方式进行加密和匹配,当用户输入明文密码登录时,security框架会自动进行此方法进行加密后和数据库密码进行匹配。

注意:此时数据库存储的密码不应该是明文了,在用户注册或修改密码时,也应该调用此加密方法对明文进行加密并存储到数据库中。

此接口提供了两个方法,一个是明文加密,一个是密文匹配,测试方法如下:

 2.1.3 登录接口编写

步骤3: 登录接口实现

        首先在security配置类中配置认证管理器AuthenticationManager,此组件的作用是通过传入前端输入的用户名和密码,调用UserDetailsServicel去后台数据库比对用户信息,如果认证成功返回数据库用户的详情信息,失败返回null。

        传入用户名和密码前需要封装成authenticationToken对象,根据认证结果编写代码处理逻辑。如果认证失败,抛出异常报错给前端,如果认证成功,返回UserDetailsServicel的LoginUser用户,通过LoginUser获取userID,生成jwt返回给前端 

// 配置认证管理器@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}

编写登录接口controller以及service实现类

@RestController
@CrossOrigin
@RequestMapping("api/v1")
@Tag(name= "用户登录接口文档") // 描述controller类的作用
@Slf4j
public class LoginController {@AutowiredLoginService loginService;@PostMapping("/authentication/login")@Operation(summary = "用户登陆接口")public R login(@RequestBody UserLoginVo user){try {Map<String, String> userLogin = loginService.login(user);return R.ok("登录成功",userLogin);}catch (Exception e){log.error(e.getMessage());return R.error(500,e.getMessage());}}
}
@Override
public Map<String, String> login(UserLoginVo user) {// AuthenticationManger authenticate 进行用户认证UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(user.getUserAccount(),user.getPassWord()); // 用户名和密码封装成 authenticationToken对象Authentication authenticate = authenticationManager.authenticate(authenticationToken); // 通过authenticationManager进行验证// 如果认证没通过,给出对应提示if(Objects.isNull(authenticate)){throw new RuntimeException("登录失败");}// 如果认证通过了,使用userid生成一个jwt, jwt存入ResponseResult返回LoginUser loginUser = (LoginUser) authenticate.getPrincipal();String userId = loginUser.getUser().getUserId().toString();String jwt = JwtUtil.createJWT(userId);// 把用户信息存入redis
//        redisService.setValue("login:" + userId, JSON.toJSONString(loginUser));redisService.setObject("login:" + userId,loginUser);// 把jwt封装成map返回前端HashMap<String, String> map = new HashMap<>();map.put("token",jwt);return map;
}

值得注意的是,由于登录接口是匿名登录的,需要进行放行,否则接口需要认证,无法访问。具体配置如下:

在配置中建议登录接口以及注册接口使用.anonymous(),仅对未认证(匿名)用户开放,已认证用户不可访问此资源。

在一些公开资源、静态资源、开放接口等建议使用.permitAll(),不需要任何身份验证,即允许已认证和未认证的用户。

// 3.设置路径权限
http.authorizeHttpRequests().requestMatchers("/api/v1/authentication/login").anonymous()  //对于登录接口,允许匿名访问.requestMatchers("/doc.html", "/swagger-ui/**", "/v3/api-docs/**").permitAll() // 允许匿名访问这些路径.anyRequest().authenticated(); // 除上面外的所有请求全部需要鉴权认证

 通过如上步骤,最终接口登录成功后会返回token

2.2 校验

2.2.1 定义jwt认证过滤器

        步骤1:获取token, 解析token获取其中的userid

        步骤2:通过userid 从 redis中获取用户信息

        步骤3: 将用户信息存入securityContextHolder

        如果在登录时存入 SecurityContextHolder,但应用是无状态的,每次请求时SecurityContextHolder 其实都是空的,无法保持状态。因为 Spring Security 的 SecurityContextHolder 只是一个线程级变量(ThreadLocal),它的生命周期仅限于当前请求的处理过程中。当一次 HTTP 请求到达服务器时:服务器分配一个线程处理请求。SecurityContextHolder 仅在该线程内存储 SecurityContext(认证信息)。当请求处理结束后,线程被回收,SecurityContextHolder 也被清空。
        正确的做法是在用户每次请求时,解析 Token,并动态地将用户信息存入 SecurityContextHolder。

@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate RedisService redisService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 获取tokenString token = request.getHeader("token");if (!StringUtils.hasText(token)) {// 没有token就放行,让后面过滤器处理filterChain.doFilter(request, response);return;}// 解析tokenString userID;try {Claims claims = JwtUtil.parseJWT(token);userID = claims.getSubject();} catch (Exception e) {throw new RuntimeException("token非法:" + e.getMessage());}// 获取用户信息
//        Object userObj  = redisService.getValue("login:" + userID);
//        LoginUser user = JSON.parseObject((String) userObj, LoginUser.class);LoginUser user = redisService.getObject(("login:" + userID), LoginUser.class);if(user==null){throw new RuntimeException("用户未登录");}// 存入SecurityContextHolder//TODO 获取权限信息封装到Authentication中UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =new UsernamePasswordAuthenticationToken(user,null,null);  // 用户、密码、权限集合SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);// 放行filterChain.doFilter(request,response);}
}

2.2.2 添加到过滤器链中

先注入过滤器实例,然后在security配置中添加倒数第二行代码

// 4. 添加认证过滤器
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

2.2.3 登出接口编写

        因为认证过滤器对用户进行校验时,会根据token获取userid,并读取登录时存入redis中的用户信息,如果redis的用户信息读不到,就会被认证过滤器拦截,根据这一特性,可以设计登出接口,即删除用户在redis中的用户信息。

        用户访问登出接口后,在SecurityContextHolder中获取userid,并删除redis中的信息

@Override
public void logout() {// 获取SecurityContextHolder中的用户IDUsernamePasswordAuthenticationToken authentication =(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();LoginUser loginUser = (LoginUser) authentication.getPrincipal();Integer userId = loginUser.getUser().getUserId();// 删除redis中的值redisService.deleteValue("login:"+userId);
}

2.3 授权

2.3.1 设置资源权限

        通过注解的方式进行资源权限的标识,决定各个接口需要什么样的权限才能访问。

        步骤1:开启注解,在security配置类添加注解 @EnableMethodSecurity  // 开启权限注解

@EnableMethodSecurity  // 开启权限注解
public class SecurityConfig  {...
}

        步骤2: 在接口上添加注解配置权限 @PreAuthorize("hasAuthority('test')")

@PreAuthorize("hasAuthority('sys:device:getList')")
@GetMapping("/device/getList")
public R getDevice(@PathVariable("id") Integer id){... 
}

步骤2中除了可以在接口上添加注解的方式设置权限,还可以基于配置进行权限设置,在securityConfig配置类中添加如下代码等同于注解:

http
.authorizeHttpRequests()
.requestMatchers("/api/v1/device/getList")
.hasAuthority("sys:device:getList");

2.3.2 封装权限信息

        将用户的权限信息封装成security需要的对象,以便在登录和校验时,能够将权限信息输入到对应的结构中。

在LoginUser类中构造有参构造器,并重写getAuthorities方法;

public class LoginUser implements UserDetails {private User user;private List<String> permissions;/*** 有参构造器* */public LoginUser(User user,List<String> permissions){this.user = user;this.permissions = permissions;}/*** 获取权限对象* */@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {log.debug("permissions:"+permissions);ArrayList<GrantedAuthority> authorities = new ArrayList<>();// 把permissions中string类型的权限信息封装成simpleGrantedAuthority对象for (String permission : permissions) {SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);authorities.add(authority);log.debug("authorities:"+authorities);}return authorities;}
}

        在UserDetailsService中调用LoginUser的有参构造器,将用户信息和权限信息注入到LoginUser实例中,以供登录时AuthenticationManager认证管理器调用返回用户权限信息。登录接口获取到认证管理器的用户权限信息后会存入redis中,在下一步中,认证过滤器会取出相关权限信息存入到SecurityContextHolder中以供后续过滤器校验。

@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//查询用户信息LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUserAccount,username);User user = userMapper.selectOne(queryWrapper);if(user==null){throw  new RuntimeException("此用户"+username+" 不存在");}//查询对应的权限信息List<PermissionSelectVo> permissions = userService.getUserByAccount(username).getPermissions();ArrayList<String> permissionList = new ArrayList<>();for (PermissionSelectVo permission:permissions){permissionList.add(permission.getPermissionCode());}// 把数据封装成UserDetailsreturn new LoginUser(user,permissionList);}

2.3.3 注入权限信息

        在用户登录时,需要把权限信息注入到LoginUser中并存入redis,因为在上一步中已经将权限信息放入LoginUser中,所以在登录后,直接将LoginUser存入redis中就自然存入了权限信息。

        在校验用户时,过滤器中从redis读出权限信息,传入UsernamePasswordAuthenticationToken中,生成带权限的用户信息存入SecurityContextHolder让其后续的过滤器进行权限校验。

// 存入SecurityContextHolder// 获取权限信息封装到Authentication中
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());  // 用户、密码、权限集合    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

3 其他功能

3.1  异常捕获

        如果在认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法进行一场处理。

        如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。所以如果我们需要自定义异常处理,给前端返回异常信息,只需要自定义AuthenticationEntryPoin 和 AccessDeniedHandler然后配置给Spring Security 即可。

3.1. 1 认证异常

@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {// 处理异常response.setStatus(HttpStatus.UNAUTHORIZED.value()); // 设置状态码response.setContentType("application/json");response.setCharacterEncoding("utf-8");response.getWriter().write(JSON.toJSONString(R.error(401,"尚未认证,请进行认证操作!")));}
}

3.1.2 权限异常 

@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {response.setStatus(HttpStatus.FORBIDDEN.value()); // 设置状态码response.setContentType("application/json");response.setCharacterEncoding("utf-8");response.getWriter().write(JSON.toJSONString(R.error(403,"无权访问!")));}
}

3.2 跨域

        在Spring Security框架中,如果已经配置了Spring的跨域处理,通常还需要针对Spring Security进行跨域配置,两者都需要配置才能确保跨域请求能够顺利通过Spring Security的安全检查。一般情况下,为了保证配置的统一性,会把配置集中使用其中一个配置,另一个配置直接放行

3.2.1 Spring跨域配置

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**")		//设置允许跨域的路径.allowedOrigins()		//设置允许跨域请求的域名,例如:allowedOrigins("http://localhost:3000", "http://example.com"),如果为空则是允许所有.allowCredentials(true) 	//是否允许发送凭证token.allowedMethods("GET","POST","PUT","DELETE")  //指定允许的 HTTP 方法.maxAge(3600 * 24);		//预检请求有效期}
}

3.2.2 Security跨域配置

// 跨域配置
http.cors().configurationSource(corsConfigurationSource()) //跨域解决方案
@Bean
CorsConfigurationSource corsConfigurationSource(){CorsConfiguration corsConfiguration = new CorsConfiguration();corsConfiguration.setAllowedHeaders(Arrays.asList("*"));corsConfiguration.setAllowedMethods(Arrays.asList("*"));corsConfiguration.setAllowedOrigins(Arrays.asList("*"));corsConfiguration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**",corsConfiguration);return source;}

3.3 csrf攻击

        前后端分离架构本身就是无状态token校验的,所以天然防范csrf攻击,无需进行设置,默认打开的,所以在前后端架构下需要设置为关闭。

http.csrf().disable(); // 关闭csrf

4 总结

本文通过引入security框架,在登录接口中通过传入页面用户登录信息(用户名、密码)给UsernamePasswordAuthenticationToken来验证用户身份,通过后返回token给前端并存入redis用户信息。其中验证的用户身份是通过实现接口UserDetailsService从数据库中获取用户信息并封装到LoginUser对象中。校验时,通过添加认证过滤器完成从token获取用户信息,并调用UsernamePasswordAuthenticationToken验证用户权限。

security的完整配置文件如下所示:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity  // 开启权限注解
public class SecurityConfig  {@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;// 注入token校验过滤器@Autowiredprivate AuthenticationEntryPoint authenticationEntryPoint;  //注入授权异常处理器@Autowiredprivate AccessDeniedHandler accessDeniedHandler; //注入认证异常处理器// 配置密码加密方式,全局自动按这个方式加密@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}// 配置认证管理器@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}// security 配置@Beanpublic SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception {// 1.前后端分离架构本身就是无状态token校验的,所以天然防范csrf攻击,可以关闭http.csrf().disable(); // 关闭csrf
//                .csrfTokenRepository(CookieCsrfTokenRepository. withHttpOnlyFalse());  // 将令牌保存到cookie中允许cookie前端获取// 2.前后端分离架构不通过Session获取SecurityContexthttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);// 3.设置路径权限http.authorizeHttpRequests().requestMatchers("/api/v1/authentication/login").anonymous()  //对于登录接口,允许匿名访问.requestMatchers("/doc.html", "/swagger-ui/**", "/v3/api-docs/**").permitAll() // 允许匿名访问这些路径.anyRequest().authenticated(); // 除上面外的所有请求全部需要鉴权认证// 4. 添加认证过滤器http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);// 5. 配置异常处理器http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)  // 配置认证失败处理器.accessDeniedHandler(accessDeniedHandler);  // 配置授权失败处理器// 6.跨域配置http.cors(); // 允许跨域return http.build();}
}

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

相关文章:

  • 杨凌企业网站开发搜索引擎调词平台
  • 网站建设公司 上海信息流广告投放
  • 太湖县住房和城乡建设局网站公司关键词排名优化
  • 网站设计师培训学校郑州整站关键词搜索排名技术
  • 招聘网站开发人员怎么找到精准客户资源
  • 个人网站建设营销型网站建设优化建站
  • 可以自己做攻略的网站百度在线识图查图片
  • 设计师能做网站前端吗b站推广网站入口
  • 自己做网站 最好的软件seo内部优化方式包括
  • 丹阳市建设局网站神马网站快速排名软件
  • 模板网站购买百度app首页
  • 网站增长期怎么做网络营销策划ppt范例
  • dz网站首页html代码在哪淘宝运营一般要学多久
  • 经营网站建设营销策划案例
  • 南京做网站联系南京乐识天津seo实战培训
  • 推广话术seo快速排名软件首页
  • 武汉城市建设网站关键信息基础设施安全保护条例
  • 网站去哪备案网络推广渠道
  • b2c网站建设湘潭网站制作
  • 广州知名网站推广app如何推广
  • 福田蒙派克哪家公司做seo
  • 南通企业做网站顾问
  • 北京有哪些网站建设杯子软文营销300字
  • 做网站的服务器还需要空间吗下载一个百度时事新闻
  • wordpress 网站卡太原做网站哪家好
  • 做网站用什么字体关联词有哪些关系
  • 网站源码 手机 微信做seo前景怎么样
  • 无锡网站制作启航好轻松seo优化排名
  • 做网站建设的销售薪水合肥网络推广公司
  • 茶楼网站模板网络公关公司联系方式