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

iframe 一直网站底部学做网站先学什么

iframe 一直网站底部,学做网站先学什么,个人网站设计模板中文,智慧景区网站建设文章目录 SpringSecurity介绍sss-security实现依赖工具类Jwt工具JSON响应工具加密工具类 用户上下文用户信息实体类用户上下文 自定义重写自定义无权限的报错自定义密码加密自定义用户类 过滤器登录过滤器权限过滤器 Service登录Service 配置类说明登录验证权限验证IP流量限制 … 文章目录 SpringSecurity介绍sss-security实现依赖工具类Jwt工具JSON响应工具加密工具类 用户上下文用户信息实体类用户上下文 自定义重写自定义无权限的报错自定义密码加密自定义用户类 过滤器登录过滤器权限过滤器 Service登录Service 配置类说明登录验证权限验证IP流量限制 sss-system模块实现Service实现登录日志实现类UserDetailsService实现类 测试登录失败测试登录成功测试其他请求测试 其他建议 SpringSecurity介绍 SpringSecurity是一款专为Java应用程序设计的身份验证和授权框架。它提供了声明式的安全访问控制解决方案使开发者能够轻松实现用户认证Authentication、授权Authorization、防止常见安全攻击以及会话管理等功能。作为Spring生态系统的一部分Spring Security无缝集成于Spring MVC、Spring Boot等项目中极大地简化了安全相关的开发工作确保应用程序具备坚实的安全防线 SpringSecurity可以轻松实现细致到按钮级别的权限控制又因为排班系统有系统管理员、门店管理员、普通员工每种角色的权限不同因此非常适合使用SpringSecurity sss-security实现 依赖 !-- Spring Security依赖 -- dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId /dependency dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdscopeprovided/scope /dependency dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId /dependency dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId /dependency dependencygroupIdcom.alibaba/groupIdartifactIdtransmittable-thread-local/artifactIdversion2.14.2/version /dependency工具类 Jwt工具 用来根据用户信息生成令牌(token)同时可以根据token解析出一些关键信息 package com.dam.utils;import io.jsonwebtoken.*; import org.springframework.util.StringUtils;import java.util.Date;/*** 生成JSON Web Token的工具类*/ public class JwtUtil {/*** JWT的默认过期时间单位为毫秒。这里设定为一年365天*/private static long tokenExpiration 365 * 24 * 60 * 60 * 1000;/*** 在实际应用中应使用随机生成的字符串*/private static String tokenSignKey dsahdashoiduasguiewu23114;/*** 从给定的JWT令牌中提取指定参数名对应的值。** param token 需要解析的JWT令牌字符串* param paramName 要提取的参数名* return 参数值字符串形式如果令牌为空、解析失败或参数不存在则返回null*/public static String getParam(String token, String paramName) {try {if (StringUtils.isEmpty(token)) {return null;}// 使用提供的密钥解析并验证JWTJwsClaims claimsJws Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);// 获取JWT的有效载荷claims其中包含了所有声明参数Claims claims claimsJws.getBody();// 提取指定参数名对应的值Object param claims.get(paramName);// 如果参数值为空则返回null否则将其转换为字符串并返回return param null ? null : param.toString();} catch (Exception e) {// 记录解析过程中的任何异常并返回nulle.printStackTrace();return null;}}/*** 根据用户信息生成一个新的JWT令牌。** param userId* param username* return*/public static String createToken(Long userId, String username, Long enterpriseId, Long storeId, int userType) { // System.out.println(createToken userType: userType);// 使用Jwts.builder()构建JWTString token Jwts.builder()// 设置JWT的主题subject此处为常量AUTH-USER.setSubject(AUTH-USER)// 设置过期时间当前时间加上预设的过期时间tokenExpiration.setExpiration(new Date(System.currentTimeMillis() tokenExpiration))// 有效载荷.claim(userId, userId).claim(username, username).claim(enterpriseId, enterpriseId).claim(storeId, storeId).claim(userType, userType)// 使用HS512算法和指定密钥对JWT进行加密.signWith(SignatureAlgorithm.HS512, tokenSignKey)// 使用GZIP压缩算法压缩JWT字符串将字符串变成一行来显示.compressWith(CompressionCodecs.GZIP)// 完成构建并生成紧凑格式的JWT字符串.compact();return token;}public static String getUserId(String token) {return getParam(token, userId);}public static String getUsername(String token) {return getParam(token, username);}public static String getEnterpriseId(String token) {return getParam(token, enterpriseId);}public static String getStoreId(String token) {return getParam(token, storeId);}public static String getUserType(String token) {return getParam(token, userType);}}JSON响应工具 ResponseUtil 的作用是为 Spring MVC 应用程序提供一种便捷的方式来构建和发送 JSON 格式的 HTTP 响应 package com.dam.utils;import com.alibaba.fastjson.JSON; import com.dam.model.result.R; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType;import javax.servlet.http.HttpServletResponse; import java.io.IOException;public class ResponseUtil {public static void out(HttpServletResponse response, R r) {ObjectMapper mapper new ObjectMapper();response.setStatus(HttpStatus.OK.value());response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);try {System.out.println(ResponseUtil r: JSON.toJSONString(r));mapper.writeValue(response.getWriter(), r);} catch (IOException e) {e.printStackTrace();}} }加密工具类 本文使用盐值加密来对用户密码进行加密盐值加密是一种增强密码安全性的技术主要用于防止密码被轻易破解特别是在密码数据库遭到泄露的情况下。其核心思想是在密码哈希过程中引入一个额外的、随机生成的值——称为“盐值”以此来增加密码的唯一性和复杂度添加盐值有如下作用 防止彩虹表攻击彩虹表是一种预先计算好的哈希值与明文密码的映射表用于快速破解已知哈希算法如MD5、SHA-1等生成的密码。通过添加盐值即使两个用户使用相同的密码由于盐值不同其哈希结果也会大相径庭从而大大削弱彩虹表的有效性。 抵御字典攻击和暴力破解盐值使得每个用户密码的哈希值都独一无二即使是最常用的密码加上随机盐值后也需要针对特定盐值进行单独破解显著增加了攻击者的计算成本。 盐值加密的实现直接使用SpringSecurity自带的工具即可 package com.dam.utils;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;/*** 加密工具*/ public class EncryptionUtil {private static BCryptPasswordEncoder passwordEncoder new BCryptPasswordEncoder();/*** 盐值MD5加密** param strSrc* return*/public static String saltMd5Encrypt(String strSrc) {return passwordEncoder.encode(strSrc);}/*** 判断原密码和加密之后的密码是否相符** param originalPassword* param encryptPassword* return*/public static boolean isSaltMd5Match(String originalPassword, String encryptPassword) {return passwordEncoder.matches(originalPassword, encryptPassword);}public static void main(String[] args) {System.out.println(EncryptionUtil.saltMd5Encrypt(123456));} }用户上下文 用户上下文主要用来记录用户的关键信息以便同线程共享无需每次从token中解析提高效率。使用阿里巴巴的TransmittableThreadLocal库替代标准的java.lang.ThreadLocal目的是确保在使用线程池或Fork/Join框架等场景下线程间可以正确地传递或“传播”ThreadLocal变量的值。这对于处理跨越多个线程的任务如异步操作、任务调度等时保持用户上下文的连续性非常重要。 用户信息实体类 package com.dam.context;import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor;/*** Author dam* create 2024/4/2 16:11*/ Data Builder AllArgsConstructor NoArgsConstructor public class UserInfoDTO {private String userId;private String userName; } 用户上下文 package com.dam.context;import com.alibaba.ttl.TransmittableThreadLocal;import java.util.Optional;/*** Author dam* create 2024/4/2 16:12*/ public class UserContext {/*** 定义一个私有的、静态的ThreadLocal变量类型为UserInfoDTO用于存储当前线程关联的用户信息*/private static final ThreadLocalUserInfoDTO USER_THREAD_LOCAL new TransmittableThreadLocal();/*** 设置用户至上下文** param user 用户详情信息*/public static void setUser(UserInfoDTO user) {USER_THREAD_LOCAL.set(user);}/*** 获取上下文中用户 ID** return 用户 ID*/public static String getUserId() {UserInfoDTO userInfoDTO USER_THREAD_LOCAL.get();// 使用Optional进行空值安全处理如果userInfoDTO不为空则提取其userId属性并返回否则返回nullreturn Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getUserId).orElse(null);}/*** 获取上下文中用户名称** return 用户名称*/public static String getUsername() {UserInfoDTO userInfoDTO USER_THREAD_LOCAL.get();return Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getUserName).orElse(null);}/*** 清理用户上下文*/public static void removeUser() {// 从ThreadLocal变量中移除当前线程关联的用户信息释放资源USER_THREAD_LOCAL.remove();} } 自定义重写 自定义无权限的报错 package com.dam.custom;import com.dam.model.result.R; import com.dam.utils.ResponseUtil; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;/*** 自定义没有权限的报错信息默认是报403*/ Component//交给spring管理 public class CustomAccessDeniedHandler implements AccessDeniedHandler {Overridepublic void handle(HttpServletRequest request, HttpServletResponse response,AccessDeniedException accessDeniedException) {// 获取请求的URIString uri request.getRequestURI();// 获取请求的方法String method request.getMethod();// 获取当前用户的用户名 // String username request.getRemoteUser();// 获取用户的IP地址 // String ip request.getRemoteAddr();// 获取用户的浏览器类型 // String userAgent request.getHeader(User-Agent);// 构造错误信息String errorMsg 没有权限访问当前资源 uri ( method );ResponseUtil.out(response, R.error(403, errorMsg));}} 自定义密码加密 package com.dam.custom;import com.dam.utils.EncryptionUtil; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component;/*** 自定义密码组件*/ Component//交给spring管理 public class CustomMd5PasswordEncoder implements PasswordEncoder {/*** 指定密码的加密方式** param rawPassword* return*/Overridepublic String encode(CharSequence rawPassword) {return EncryptionUtil.saltMd5Encrypt(rawPassword.toString());}/*** 判断用户所输入的密码和加密之后的密码是否相同** param rawPassword* param encodedPassword* return*/Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {boolean equals EncryptionUtil.isSaltMd5Match(rawPassword.toString(), encodedPassword);if (equals true) {return true;} else {System.out.println(登录密码验证不通过密码错误); // System.out.println(原密码rawPassword rawPassword); // System.out.println(原密码加密 encrypt); // System.out.println(数据库中已加密的密码encodedPassword encodedPassword);return false;}} } 自定义用户类 继承security的User增加一些自己的信息方便后续使用security的User主要用来存储用户名、密码、权限信息 package com.dam.custom;import com.dam.model.entity.system.UserEntity; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User;import java.util.Collection;/*** 自定义用户对象*/ public class CustomUser extends User {/*** 我们自己的用户实体对象要调取用户信息时直接获取这个实体对象*/private UserEntity sysUser;public CustomUser(UserEntity sysUser, Collection? extends GrantedAuthority authorities) {super(sysUser.getUsername(), sysUser.getPassword(), authorities);this.sysUser sysUser;}public UserEntity getSysUser() {return sysUser;}public void setSysUser(UserEntity sysUser) {this.sysUser sysUser;}} 过滤器 登录过滤器 package com.dam.filter;import com.alibaba.fastjson.JSON; import com.dam.constant.RedisConstant; import com.dam.custom.CustomUser; import com.dam.model.entity.system.UserEntity; import com.dam.model.enums.ResultCodeEnum; import com.dam.model.result.R; import com.dam.model.vo.system.LoginVo; import com.dam.service.RecordLoginLogService; import com.dam.utils.JwtUtil; import com.dam.utils.ResponseUtil; import com.dam.utils.ip.IpUtil; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.authentication.*; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;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.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit;/*** 登录过滤器继承UsernamePasswordAuthenticationFilter对用户名密码进行登录校验*/ public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {private StringRedisTemplate redisTemplate;/*** 登录日志服务用于记录用户的登录情况* 方便对系统的用户活跃情况进行统计*/private RecordLoginLogService loginLogService;/*** 构造方法** param authenticationManager 认证管理器负责实际的用户身份验证*/public TokenLoginFilter(AuthenticationManager authenticationManager, StringRedisTemplate redisTemplate, RecordLoginLogService sysLoginLogService) { // System.out.println(登录验证过滤);this.setAuthenticationManager(authenticationManager);this.redisTemplate redisTemplate;this.loginLogService sysLoginLogService;// 不只是可以postthis.setPostOnly(false);// 指定登录接口及提交方式可以指定任意路径我们默认的登陆路径this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(/system/login/login, POST));}/*** 登录认证覆盖父类实现** param req HTTP请求对象* param res HTTP响应对象* return 认证后的Authentication对象* throws AuthenticationException 认证过程中抛出的异常*/Overridepublic Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)throws AuthenticationException {System.out.println(进行登录认证-----------------------------------------------------------------------------------------);try {// 使用Jackson ObjectMapper从请求流中反序列化登录信息对象LoginVo loginVo new ObjectMapper().readValue(req.getInputStream(), LoginVo.class);System.out.println(loginVo: JSON.toJSONString(loginVo));// 判断登录验证码是否正确String redisKey RedisConstant.Verification_Code loginVo.getUuid();String verificationCode redisTemplate.opsForValue().get(redisKey);if (verificationCode null) {throw new AuthenticationServiceException(验证码已经失效请刷新之后再重新登录);}if (!verificationCode.toLowerCase().equals(loginVo.getVerificationCode().toLowerCase())) {throw new AuthenticationServiceException(验证码输入不正确);}// 创建UsernamePasswordAuthenticationToken封装登录信息Authentication authenticationToken new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword()); // System.out.println(authenticationToken: authenticationToken.toString());// 调用父类的authenticate方法通过认证管理器进行实际的身份验证会判定登陆密码和数据库密码是否一致Authentication authenticate this.getAuthenticationManager().authenticate(authenticationToken);System.out.println(登录验证成功);return authenticate;} catch (IOException e) {System.out.println(登录验证失败);throw new RuntimeException(e);}}/*** 登录成功后的处理方法覆盖父类实现** param request* param response* param chain* param auth 当前验证对象* throws IOException* throws ServletException*/Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,Authentication auth) {System.out.println(登录成功生成token------------------------------------------------------------------------------------------);// 获取当前用户信息CustomUser customUser (CustomUser) auth.getPrincipal();// 保存权限数据到redisString redisKey RedisConstant.AUTHORITY_PERMISSION customUser.getUsername();System.out.println(保存用户权限到redis中redisKey redisKey);//设置缓存过期时间是十五天redisTemplate.opsForValue().set(redisKey,JSON.toJSONString(customUser.getAuthorities()),15,TimeUnit.DAYS);// 生成tokenUserEntity sysUser customUser.getSysUser();String token JwtUtil.createToken(sysUser.getId(), sysUser.getUsername(), sysUser.getEnterpriseId(), sysUser.getStoreId(), sysUser.getType());System.out.println(token: token);// 记录登录日志loginLogService.recordLoginLog(customUser.getUsername(), 0, IpUtil.getIpAddress(request), 登录成功, sysUser.getEnterpriseId(), sysUser.getStoreId());// 将token返回给前端MapString, Object map new HashMap();map.put(token, token);ResponseUtil.out(response, R.ok().addData(data, map));}/*** 登录失败后的处理方法覆盖父类实现** param request HTTP请求对象* param response HTTP响应对象* param e 认证失败异常*/Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,AuthenticationException e) {System.out.println(登录失败------------------------------------------------------------------------------------------);System.out.println(失败原因 e.getMessage());// 分析具体失败原因并提供对应的错误信息String errorMessage;if (e instanceof BadCredentialsException) {errorMessage 用户名或密码错误;} else if (e instanceof DisabledException) {errorMessage 账户已被禁用请联系管理员;} else if (e instanceof LockedException) {errorMessage 账户已被锁定请联系管理员;} else if (e instanceof AuthenticationServiceException) {errorMessage 认证服务异常请稍后重试;} else {errorMessage 登录失败;}ResponseUtil.out(response, R.error(ResultCodeEnum.DATA_ERROR.getCode(), errorMessage));} }权限过滤器 这段代码定义了一个名为TokenAuthenticationFilter的类它继承自Spring Security的OncePerRequestFilter用于处理每个HTTP请求解析并验证请求头中的Token以及将认证信息放入Spring Security的上下文中。 package com.dam.filter;import com.alibaba.fastjson.JSON; import com.dam.constant.RedisConstant; import com.dam.context.UserContext; import com.dam.context.UserInfoDTO; import com.dam.model.enums.ResultCodeEnum; import com.dam.model.result.R; import com.dam.utils.JwtUtil; import com.dam.utils.ResponseUtil; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; 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.ArrayList; import java.util.List; import java.util.Map;/*** 认证解析token过滤器* OncePerRequestFilter每次请求都要过滤*/ public class TokenAuthenticationFilter extends OncePerRequestFilter {private StringRedisTemplate redisTemplate;public TokenAuthenticationFilter(StringRedisTemplate redisTemplate) {this.redisTemplate redisTemplate;}/*** 重写OncePerRequestFilter的doFilterInternal方法处理每个请求* param request* param response* param chain* throws IOException* throws ServletException*/Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println(权限验证过滤);// logger.info(uri: request.getRequestURI()); // System.out.println(request.getRequestURI():request.getRequestURI());//如果是登录接口直接放行if (/system/login/login.equals(request.getRequestURI())) {chain.doFilter(request, response);return;}// 调用getAuthentication方法尝试从请求中获取有效的Token并解析出认证信息UsernamePasswordAuthenticationToken authentication getAuthentication(request);if (null ! authentication) {// --if--如果获取到有效的认证信息 // System.out.println(request: request.toString()); // System.out.println(response: response.toString()); // System.out.println(authentication: authentication.toString());// 将认证信息放入Spring Security的SecurityContextHolder中以便后续请求链中使用SecurityContextHolder.getContext().setAuthentication(authentication);// 继续执行过滤链中的其他过滤器和目标处理器chain.doFilter(request, response);} else {// 如果未能获取到有效的认证信息返回失败响应ResponseUtil.out(response, R.ok().addData(data, ResultCodeEnum.PERMISSION));}}/*** 看看是否有token根据token是否可以获取到用户获取不到再进行账号密码登录** param request* return*/private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {System.out.println(获取权限信息------------------------------------------------------------------------------------------);// token置于header里String token request.getHeader(token); // logger.info(token: token);if (!StringUtils.isEmpty(token)) {String username JwtUtil.getUsername(token); // logger.info(username: username);if (!StringUtils.isEmpty(username)) {// 获取授权信息String redisKey RedisConstant.AUTHORITY_PERMISSION username;// 权限字符串String authoritiesString redisTemplate.opsForValue().get(redisKey);if (authoritiesString null) {return null;}// 解析权限字符串为具体的权限集合ListMap mapList JSON.parseArray(authoritiesString, Map.class);ListSimpleGrantedAuthority authorities new ArrayList();for (Map map : mapList) {authorities.add(new SimpleGrantedAuthority((String) map.get(authority)));}// 存储用户上下文信息String userId JwtUtil.getUserId(token);UserContext.setUser(UserInfoDTO.builder().userId(userId).userName(username).build());// 构建并返回UsernamePasswordAuthenticationToken对象包含用户名、空密码此处无需密码因为已通过Token验证和权限列表return new UsernamePasswordAuthenticationToken(username, null, authorities);}}// 如果未能成功解析Token或获取权限信息返回nullreturn null;} }Service 登录Service package com.dam.service;import com.dam.model.entity.system.LoginLogEntity;public interface RecordLoginLogService {/*** 记录登录信息** param username 用户名* param status 状态* param ipaddr ip* param message 消息内容* return*/void recordLoginLog(String username, Integer status, String ipaddr, String message,Long enterpriseId,Long storeId);LoginLogEntity getById(Long id);}配置类 里面有一些接口当时偷懒没有在数据库里面配置相应的权限为了开发的时候方便测试将其放在了忽略接口路径后续需要修改。 package com.dam.config;import com.dam.configuration.IpFlowControlConfiguration; import com.dam.custom.CustomAccessDeniedHandler; import com.dam.custom.CustomMd5PasswordEncoder; import com.dam.filter.IpFlowLimitFilter; import com.dam.filter.TokenAuthenticationFilter; import com.dam.filter.TokenLoginFilter; import com.dam.service.RecordLoginLogService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.builders.WebSecurity; 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.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;Configuration EnableWebSecurity //开启SpringSecurity的默认行为 EnableGlobalMethodSecurity(prePostEnabled true) // 开启方法级别的安全注解如PreAuthorize, PostAuthorize public class WebSecurityConfig extends WebSecurityConfigurerAdapter {Autowired // Qualifier(systemUserDetailsServiceImpl) // 指定实现类private UserDetailsService userDetailsService;Autowiredprivate CustomMd5PasswordEncoder customMd5PasswordEncoder;Autowiredprivate StringRedisTemplate redisTemplate;Autowired // Qualifier(systemRecordLoginLogServiceImpl) // 指定实现类private RecordLoginLogService loginLogService;Autowiredprivate CustomAccessDeniedHandler customAccessDeniedHandler;Autowiredprivate IpFlowControlConfiguration ipFlowControlConfiguration;/*** 创建并返回一个AuthenticationManager实例此方法由父类WebSecurityConfigurerAdapter提供** return* throws Exception*/BeanOverrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}Overrideprotected void configure(HttpSecurity http) throws Exception {// 这是配置的关键决定哪些接口开启防护哪些接口绕过防护http// 自定义没有权限时的报错信息.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler)// 关闭CSRF跨站请求伪造防护.and().csrf().disable()// 开启跨域以便前端调用接口网关已经做了全局跨域//.cors().and()// 设置访问控制规则.authorizeRequests()// 指定某些接口不需要通过验证即可访问。登陆接口肯定是不需要认证的在下面统一配置了//.antMatchers(/system/login/login).permitAll()// 这里意思是其它所有接口需要认证才能访问.anyRequest().authenticated()// 添加自定义过滤器按照顺序依次执行.and()// TokenAuthenticationFilter放到UsernamePasswordAuthenticationFilter的前面这样做就是为了除了登录的时候去查询数据库外其他时候都用token进行认证。.addFilterBefore(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class)// IpFlowLimitFilter在TokenAuthenticationFilter之前进行IP流量限制.addFilterBefore(new IpFlowLimitFilter(redisTemplate, ipFlowControlConfiguration), TokenAuthenticationFilter.class)// TokenLoginFilter处理登录请求使用AuthenticationManager进行认证并记录登录日志.addFilter(new TokenLoginFilter(authenticationManager(), redisTemplate, loginLogService));// 禁用session 采用无状态会话管理适用于基于Token的身份验证http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);}Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// 指定 UserDetailService 和 加密器auth.userDetailsService(userDetailsService).passwordEncoder(customMd5PasswordEncoder);}/*** 配置哪些请求不拦截* 重写configure(WebSecurity)方法* 使用web.ignoring().antMatchers(...)指定一系列接口路径* 这些路径的请求将不会经过Spring Security的过滤链即不受安全约束。** param web* throws Exception*/Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers(/favicon.ico,/swagger-resources/**,/webjars/**, /v2/**,/swagger-ui.html/**,/doc.html,/system/login/sendMailCode,/system/login/usernameCheck/**,/system/login/regist,/system/login/enterpriseRegister,/system/user/getByOpenid,/system/user/getUserEntityByToken,/system/user/bindWechat,/system/login/generateVerificationCode,/api/ucenter/wx/callback,/scheduling/imserver/**,//定时任务需要访问/scheduling/schedulingdate/judgeOneDateIsRest,/scheduling/shiftuser/listStaffWorkDtoByWorkDate,/system/user/getUserIdAndMailMapByUserIdList,/system/user/listUserEntityByStoreId,/system/menu/storeAuthoritiesToRedis,/thirdParty/oss/policy,/thirdParty/mail/send,/api/ucenter/wx/**,/thirdParty/mail/send);} }说明 登录验证 使用了SpringSecurity之后不需要再自己实现登录方法因为在上面已经完成了验证码校验、密码校验 权限验证 系统的权限控制方式是将菜单权限绑定到角色中然后再将角色分配给用户。在登录成功之后将用户对应的权限标识查询出来并存储到Redis中当用户访问需要权限的接口时SpringSecurity会从Redis中获取用户有的权限标识然后判断用户是否有接口对应权限没有则报没有权限错误 那么怎么给接口做权限控制呢实现非常简单只需要在接口上面添加注解和相应的权限标识如PreAuthorize(hasAuthority(bnt.sysMenu.list)) /*** 列表*/ RequestMapping(/list) PreAuthorize(hasAuthority(bnt.sysMenu.list)) public R list(RequestParam MapString, Object params) {PageUtils page menuService.queryPage(params);return R.ok().addData(page, page); }IP流量限制 如果看过我的代码的同学可以还有一些IP流量限制的代码我没有讲解感兴趣的同学可以查看【智能排班系统】基于Redis的increment命令和lua脚本实现IP限流 sss-system模块实现 Service实现 登录日志实现类 package com.dam.service.impl.security;import com.dam.dao.LoginLogDao; import com.dam.model.entity.system.LoginLogEntity; import com.dam.service.RecordLoginLogService; import com.dam.utils.ServletUtils; import eu.bitwalker.useragentutils.UserAgent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;//Service(systemRecordLoginLogServiceImpl) Service public class RecordLoginLogServiceImpl implements RecordLoginLogService {Autowiredprivate LoginLogDao loginLogDao;Overridepublic void recordLoginLog(String username, Integer status, String ipaddr, String message, Long enterpriseId, Long storeId) {LoginLogEntity sysLoginLog new LoginLogEntity();sysLoginLog.setUsername(username);sysLoginLog.setIpaddr(ipaddr);sysLoginLog.setMsg(message);// 日志状态sysLoginLog.setStatus(status);/// 获取用户的浏览器和操作系统final UserAgent userAgent UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader(User-Agent));// 获取客户端操作系统String os userAgent.getOperatingSystem().getName();sysLoginLog.setOs(os);// 获取客户端浏览器String browser userAgent.getBrowser().getName();sysLoginLog.setBrowser(browser);/// 存储用户的企业 门店信息if (enterpriseId ! null) {sysLoginLog.setEnterpriseId(enterpriseId);}if (storeId ! null) {sysLoginLog.setStoreId(storeId);}loginLogDao.insert(sysLoginLog);}// Override // public IPageLoginLogEntity selectPage(PageLoginLogEntity pageParam, LoginLogQueryVo sysLoginLogQueryVo) { // //获取条件值 // String username sysLoginLogQueryVo.getUsername(); // String createTimeBegin sysLoginLogQueryVo.getCreateTimeBegin(); // String createTimeEnd sysLoginLogQueryVo.getCreateTimeEnd(); // //封装条件 // QueryWrapperLoginLogEntity wrapper new QueryWrapper(); // if (!StringUtils.isEmpty(username)) { // wrapper.like(username, username); // } // if (!StringUtils.isEmpty(createTimeBegin)) { // wrapper.ge(create_time, createTimeBegin); // } // if (!StringUtils.isEmpty(createTimeBegin)) { // wrapper.le(create_time, createTimeEnd); // } // //调用mapper方法 // IPageLoginLogEntity pageModel loginLogDao.selectPage(pageParam, wrapper); // return pageModel; // }Overridepublic LoginLogEntity getById(Long id) {return loginLogDao.selectById(id);}// Override // public PageUtils queryPage(MapString, Object params) { // IPageLoginLogEntity page this.page( // new QueryLoginLogEntity().getPage(params), // new QueryWrapperLoginLogEntity().orderByDesc(create_time) // ); // // return new PageUtils(page); // } } 这段代码定义了一个名为UserDetailsServiceImpl的类实现了Spring Security的UserDetailsService接口用于根据用户名加载用户详细信息包括用户权限 package com.dam.service.impl.security;import com.dam.custom.CustomUser; import com.dam.model.entity.system.UserEntity; import com.dam.model.enums.system.UserCodeEnum; import com.dam.service.MenuService; import com.dam.service.UserService; import org.springframework.beans.factory.annotation.Autowired; 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.Component; import org.springframework.stereotype.Service;import java.util.ArrayList; import java.util.List;Service public class UserDetailsServiceImpl implements UserDetailsService {Autowiredprivate UserService sysUserService;Autowiredprivate MenuService menuService;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {System.out.println(根据用户名查询用户授权信息----------------------------------------------------------------------------);// 通过UserService根据用户名查询用户信息UserEntity sysUser sysUserService.getUserInfoByUsername(username);// 用户信息不存在时抛出异常if (null sysUser) {throw new UsernameNotFoundException(用户名不存在请检查输入是否错误);}// 判断用户状态如被禁用则抛出异常if (sysUser.getStatus().intValue() UserCodeEnum.STATUS_BAN.getCode().intValue()) {throw new RuntimeException(账号已被禁用请咨询管理员);}// 根据userId查询操作权限ListString userPermsList menuService.getUserButtonList(sysUser.getId());System.out.println(用户可操作按钮userPermsList: userPermsList);// 转化成security要求的格式数据ListSimpleGrantedAuthority authorities new ArrayList();for (String perm : userPermsList) {authorities.add(new SimpleGrantedAuthority(perm.trim()));}return new CustomUser(sysUser, authorities);}} UserDetailsService实现类 package com.dam.service.impl.security;import com.dam.custom.CustomUser; import com.dam.model.entity.system.UserEntity; import com.dam.model.enums.system.UserCodeEnum; import com.dam.service.MenuService; import com.dam.service.UserService; import org.springframework.beans.factory.annotation.Autowired; 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;Service public class UserDetailsServiceImpl implements UserDetailsService {Autowiredprivate UserService sysUserService;Autowiredprivate MenuService menuService;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {System.out.println(根据用户名查询用户授权信息----------------------------------------------------------------------------);// 通过UserService根据用户名查询用户信息UserEntity sysUser sysUserService.getUserInfoByUsername(username);// 用户信息不存在时抛出异常if (null sysUser) {throw new UsernameNotFoundException(用户名不存在请检查输入是否错误);}// 判断用户状态如被禁用则抛出异常if (sysUser.getStatus().intValue() UserCodeEnum.STATUS_BAN.getCode().intValue()) {throw new RuntimeException(账号已被禁用请咨询管理员);}// 根据userId查询操作权限ListString userPermsList menuService.getUserButtonList(sysUser.getId());System.out.println(用户可操作按钮userPermsList: userPermsList);// 转化成security要求的格式数据ListSimpleGrantedAuthority authorities new ArrayList();for (String perm : userPermsList) {authorities.add(new SimpleGrantedAuthority(perm.trim()));}return new CustomUser(sysUser, authorities);}} 测试 读者们可能看到上面的代码整个人是一脸懵逼这么多类这么多方法究竟是怎么执行的下面我通过测试给出了调用链路大家可以跟着顺序来理解上面的代码 登录失败测试 模拟密码错误代码的执行顺序如下 调用 TokenAuthenticationFilter 的 doFilterInternal调用 TokenLoginFilter 的 attemptAuthentication调用 UserDetailsServiceImpl 的 loadUserByUsername调用 CustomMd5PasswordEncoder 的 matches调用 TokenLoginFilter 的 unsuccessfulAuthentication登录成功测试 输入正确密码代码的执行顺序如下 调用 TokenAuthenticationFilter 的 doFilterInternal调用 TokenLoginFilter 的 attemptAuthentication调用 UserDetailsServiceImpl 的 loadUserByUsername调用 CustomMd5PasswordEncoder 的 matches调用 TokenLoginFilter 的 successfulAuthentication调用 RecordLoginLogServiceImpl 的 recordLoginLog其他请求测试 发起一个除了登录之外的请求代码的执行顺序如下 调用 TokenAuthenticationFilter 的 doFilterInternal调用 TokenAuthenticationFilter 的 getAuthentication其他建议 如果对用户权限标识存储有什么不理解的地方可以参考【智能排班系统】数据库设计的菜单表、角色表、用户表、角色菜单中间表、用户角色中间表。
http://www.pierceye.com/news/428511/

相关文章:

  • 个人网站开发是学什么语言网站建设 总体思路
  • 网站怎么留住用户在什么地方可以接到做网站的活
  • jsp页面如何做网站pv统计wordpress4.7.10
  • 澄海网站建设用asp做网站怎么美观
  • 未满18岁能申请网站备案吗网站做qq链接
  • 网络公司网站开发互联网技术专业学什么
  • 校园网站建设详细的设计方案php网站开发实战
  • 网站设计广州量计价格网站可信度建设
  • 门业网站 源码做企业网站必须要座机吗
  • 广东网站开发推荐wordpress快速加载
  • php网站开发外包北京网站建设迈程网络
  • 微信开发者平台取消授权seo资源网站排名
  • 将网站源码下载下来如何使用网站seo链接购买
  • 可信网站认证必须做苏州那里可以建网站
  • 手机网站底部代码有名的网站建设
  • 做一个网站需要多长时间网站制作有哪些种类
  • 做微信推送的网站小刘网站建设
  • 18款禁用软件app网站入口台州网站推广排名
  • 网站的服务内容济南网站制作方案
  • 微网站模板 phpwordpress 支付宝
  • wordpress dux主题破解安装全屏网站 图片优化
  • 一键建站公司做网站 怎么做留言
  • 制作的网站西安网页设计培训哪里有
  • 株洲市住房和城乡建设局门户网站中国建设银行官网站大同
  • 北京响应式网站制作公司wordpress邀请码注册
  • 衡阳网站开发有哪些公司怎么建设淘客自己的网站、
  • 国内扁平化网站欣赏什么站做咨询网站好
  • 评价校园网站建设范例wordpress插件获取数据库
  • 网站开发具体问题重庆装修公司排名前十名
  • 萝卜建站织梦网站采集如何做