security的jwt验证:
总体来说,我们加入依赖项,security就已经开始生效了,但是使用的默认的UserDetails和UserDetailsService,
一 、我们只要继承UserDetailsService,在数据库中查询用户和权限列表,封装成UserDetails的实现类,返回就可以实现,security验证的接管,最多在security配置类中,放行一些路径。
二 、如果自己想重新整个验证路径,那么在security配置类,暴露一个AuthenticationManager,然后自己写验证流程。如:
@Service public class LoginServiceImpl implements LoginService { @Autowired AuthenticationManager authenticationManager; //自定义的spring security登录流程 @Override public Map<String, Object> login(String user_name, String password) throws Exception { //1.封装Authentication对象 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user_name,password); //2.进行校验 (会自动调用UserDetailsService.loadUserByUsername) Authentication authenticate =authenticationManager.authenticate(authentication); //3.如果校验为空,认证失败抛出异常 if(Objects.isNull(authenticate)) { throw new RuntimeException("登录失败!"); } //4.取出,存入的用户对象 LoginUser loginUser = (LoginUser)authenticate.getPrincipal(); //5.生产jwt字符串 String loginUserStr = JSON.toJSONString(loginUser); //JwtUtils String jwt = JwtUtils.createJWT(loginUserStr, null); //System.out.println(jwt); //解析 //Claims claims = JwtUtils.parseJWT(jwt); //System.out.println(claims); //System.out.println(claims.getSubject()); Map<String,Object> loginObj = new HashMap<String,Object>(); loginObj.put("admin",loginUser.getAdmins()); loginObj.put("jwt",jwt); return loginObj; } }
这个自定义验证流程,会自动找我们已经暴露出来的UserDetails和UserDetailsService。
最后我们再完善一下security的配置类,排除一些路径,添加一些验证成功和失败的处理器Handler,在如果需要其他一些验证过滤器(验证码,jwt),可以加到原有的过滤器链中。
============下来我们就按照自定义的验证,完善一下其周边的一些配置。
1、上面的自定义验证,也会自动调用UserDetailsService,随意我们也必须重写UserDetailsService和UserDetails.
UserDetailsService:
@Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired AdminsService adminsService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //1.查询用户信息 AdminsEntity admins = adminsService.getAdminByName(username); if(Objects.isNull(admins)){ throw new RuntimeException("用户名错误!"); } //2.查询用户权限列表 //List<Permission> permissions = permissionDao.findByUserid(myUser.getUserid()); //Collection<? extends GrantedAuthority> authorities=permissions.stream().map(item->new SimpleGrantedAuthority(item.getPerCode())).collect(Collectors.toList()); //System.out.println(authorities); System.out.println(admins); LoginUser loginUser = new LoginUser(admins,admins.getId(),null); return loginUser; } }
UserDetails:
@Data @AllArgsConstructor @NoArgsConstructor public class LoginUser implements UserDetails { public AdminsEntity admins; public Integer user_id; @JsonIgnore public List<PermissionEntity> permissions; @Override public Collection<? extends GrantedAuthority> getAuthorities() { if(permissions == null) return null; Collection<? extends GrantedAuthority> authorities=permissions.stream().map(item->new SimpleGrantedAuthority(item.getPerCode())).collect(Collectors.toList()); return authorities; } @Override public String getPassword() { return admins.getPassWord(); } @Override public String getUsername() { return admins.getUserName(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
2、其中最重要验证环节,要写的都写完了,周边的一些配置,(使用的密码加密类,登录成功和失败的处理器,加入jwt或验证码过滤器)
/** * spring security配置 */ @Configuration @AllArgsConstructor @EnableWebSecurity @EnableMethodSecurity public class SecurityConfig { /** * 自定义用户认证逻辑 */ @Autowired(required=true) public UserDetailsServiceImpl userDetailsServiceImpl; /** * 验证码验证逻辑过滤器 */ @Autowired(required=true) public ValidateCodeFilter validateCodeFilter; /** * 认证失败处理类(jwt)与web用其一 */ @Autowired private AuthenticationEntryPointImpl unauthorizedHandler; /** * 认证失败处理类(web)与jwt用其一 */ @Autowired private CustomAuthenticationEntryPoint customAuthenticationEntryPoint; /** * 退出处理类 */ @Autowired private LogoutSuccessHandlerImpl logoutSuccessHandler; /** * token认证过滤器 */ @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; /** * 跨域过滤器 */ @Autowired private CorsFilter corsFilter; /** * 允许匿名访问的地址 */ private final String[] paths = { "/druid/**", "/system/captcha/line", "/druid/login.html/**", "/system/login", "/js/**", "/*/*.json", "/*/*.yml", "/prims/**", "/type/**", "/system/file/**", "/diagram-viewer/**", "/images/**", "/api/login/**", "/api/file/**", "/css/**", "/*/*.ico", "/swagger-resources/**", "/swagger/**", "/swagger-ui/**", "/webjars/**", "/v3/**", "/v2/**", "/doc.html/**" }; @Autowired private DataSource dataSource; /** * 获取AuthenticationManager(认证管理器),登录时认证使用 * @return * @throws Exception */ @Bean public AuthenticationManager authenticationManager() throws Exception { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setPasswordEncoder(bCryptPasswordEncoder()); provider.setUserDetailsService(userDetailsServiceImpl); return new ProviderManager(provider); } /** * 获取AuthenticationManager(认证管理器),登录时认证使用 * @param authenticationConfiguration * @return * @throws Exception */ //方式一 (新方式,默认AuthenticationManager使用的事暴露出来的UserDetailsService和PasswordEncoder) // @Bean // public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { // return authenticationConfiguration.getAuthenticationManager(); // } //方法二:(旧方式,已经不继承父类) // @Override // protected void configure(AuthenticationManagerBuilder auth) throws Exception // { // auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); // } /** * anyRequest | 匹配所有请求路径 * access | SpringEl表达式结果为true时可以访问 * anonymous | 匿名可以访问 * denyAll | 用户不能访问 * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 * hasRole | 如果有参数,参数表示角色,则其角色可以访问 * permitAll | 用户可以任意访问 * rememberMe | 允许通过remember-me登录的用户访问 * authenticated | 用户登录后可访问 */ @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 注解标记允许匿名访问的url //http.authorizeHttpRequests(conf -> conf.requestMatchers(paths).permitAll()); //http.csrf(httpSecurityCsrfConfigurer -> httpSecurityCsrfConfigurer.disable()); http // 基于 web,需要 csrf (其实不用配置,默认支持),基于jwt,需要禁用 .csrf(AbstractHttpConfigurer::disable) // 基于 web,需要 session (其实不用配置,默认支持),基于jwt,配置为SessionCreationPolicy.STATELESS .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 下面开始设置权限过滤请求 .authorizeHttpRequests(authorize -> authorize // 请求放开 // 对于登录login 注册register 验证码captchaImage 允许匿名访问 .requestMatchers("/sys/login","/login?error=true2","/login?error=AuthenticationEntryPointImpl","/login?**","/dologin","/logout","/register", "/captchaImage","/test/*").permitAll() // 静态资源,可匿名访问 .requestMatchers(HttpMethod.GET, "/", "/*.html", "/*/*.html", "/*/*.css", "/*/*.js", "/profile/**").permitAll() .requestMatchers("/css/**","/js/**","/img/**","/uploads/*s*","*/favicon.ico","/favicon.ico").permitAll() .requestMatchers("/swagger-ui.html", "/swagger-resources/*", "/webjars/*", "/*/api-docs", "/druid/*").permitAll() // 其他地址的访问均需验证权限 .anyRequest().authenticated() ); //请求过滤规则 //指定登录表单的规则 (新版配置用lambda表达式, 只做记录,用于web验证) //http.formLogin(fl->fl//这个路径必须和登录表单的提交路径一致。 //.loginProcessingUrl("/dologin") //设置自定义登录界面 //.loginPage("/login") //登录成功后转发的路径 //.successForwardUrl("/main") //登录失败跳转地址 (做记录) //.failureForwardUrl("/login?error=true") //.failureUrl("/login?error=true") //.failureHandler(new SimpleUrlAuthenticationFailureHandler("/login?error=loginFailure")) //.failureHandler(new AuthenticationEntryPointFailureHandler(customAuthenticationEntryPoint)) //.failureHandler(customAuthenticationFailureHandler) //.permitAll()); //登出配置(web) http.logout(lt->lt.logoutUrl("/logout").logoutSuccessUrl("/").clearAuthentication(true)); //记住我功能 //https://blog.csdn.net/weixin_47826286/article/details/127799842 // http.rememberMe(re->re.tokenRepository(persistentTokenRepository()) // // 有效时间:单位s // .tokenValiditySeconds(60) // .userDetailsService(userDetailsServiceImpl)); //认证失败处理器 (之前版本配置) //http.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint);//web形式用customAuthenticationEntryPoint,jwt形式用unauthorizedHandler //认证失败处理器 (7.0新版本配置,用lambda表达式) //authenticationEntryPoint认证失败的处理逻辑,accessDeniedHandler访问拒绝的处理逻辑 http.exceptionHandling( exceptions -> exceptions.authenticationEntryPoint(unauthorizedHandler) .accessDeniedHandler(new MyAccessDeniedHandler()) ); // 配置登录之前添加一个验证码的过滤器 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class); // jwt 校验 http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } /** * 强散列哈希加密实现 */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 记住我功能的数据库配置(在配置项中rememberMe最好配置token过期时间) * @return */ @Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); tokenRepository.setDataSource(dataSource); // 如果token表不存在,使用下面语句可以初始化该表;若存在,请注释掉这条语句,否则会报错。 //tokenRepository.setCreateTableOnStartup(true); return tokenRepository; } @Bean public WebSecurityCustomizer webSecurityCustomizer() { //return (web) -> web.ignoring().antMatchers("/css/**","/js/**","/img/**","/uploads/**","**/favicon.ico"); //之前配置 return (web) -> web.ignoring().requestMatchers("/css/**","/js/**","/img/**","/uploads/**","*/favicon.ico"); //新配置 } /** * 配置跨源访问(CORS) * @return */ @Bean public CorsConfigurationSource corsConfigurationSource() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); return source; } }
3、下列是配置类中设置的,验证成功或失败处理器,和过滤器.
JwtAuthenticationTokenFilter:
/** * token过滤器 验证token有效性 * */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { // @Autowired // private TokenService tokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // LoginUser loginUser = tokenService.getLoginUser(request); // if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) // { // tokenService.verifyToken(loginUser); // UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); // authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); // SecurityContextHolder.getContext().setAuthentication(authenticationToken); // } //1.放行某些地址(入登录地址) String uri = request.getRequestURI(); if(uri.equals("/sys/login") || uri.equals("/sys/verify")){ chain.doFilter(request, response); return; } //2.接受参数 String token = request.getHeader("Authorization"); //System.out.println(token); if(!StringUtils.hasText(token)){ throw new RuntimeException("token为空!"); } //3.验证非空 LoginUser loginUser = null; //4.解析校验参数 try { Claims claims = JwtUtils.parseJWT(token); //System.out.println(claims); //System.out.println(claims.getSubject()); String loginUserStr = claims.getSubject(); //System.out.println(loginUserStr); loginUser = JSON.parseObject(loginUserStr,LoginUser.class); } catch (Exception e) { throw new RuntimeException("token校验失败"); } //5.把验证完的userDetails 再次防暑spring Security上下文中 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser,null,null); SecurityContextHolder.getContext().setAuthentication(authentication); //6.放行过滤器连 chain.doFilter(request, response); } }
ValidateCodeFilter:这个和生成验证码配套使用(web验证码存在sessiion,jwt形式验证码存redis等)
这里演示的验证码在生成时存在session中.
@Component public class ValidateCodeFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //得到请求地址 String requestURI = request.getRequestURI(); System.out.println("requestURL:" + requestURI+" Method:"+request.getMethod()); // 非登录请求不校验验证码,直接放行 if ( ("/dologin".equals(request.getRequestURI()) && request.getMethod().equalsIgnoreCase("POST") )) { try { //校验验证码 verificationCode(request); //验证码校验通过后,对请求进行放行 filterChain.doFilter(request, response); } catch (AuthenticationException exception) { new CustomAuthenticationFailureHandler().onAuthenticationFailure(request, response, exception); return; } }else{ filterChain.doFilter(request, response); } } public void verificationCode (HttpServletRequest httpServletRequest) throws AuthenticationException { HttpSession session = httpServletRequest.getSession(); String savedCode = (String) session.getAttribute("captcha"); if (!StringUtils.hasLength(savedCode)) { // 随手清除验证码,不管是失败还是成功,所以客户端应在登录失败时刷新验证码 session.removeAttribute("captcha"); } String requestCode = httpServletRequest.getParameter("captcha"); // 校验不通过抛出异常 if (!(StringUtils.hasLength(requestCode) && StringUtils.hasLength(savedCode) && requestCode.equals(savedCode))) { //throw new AuthenticationServiceException("验证码错误.."); throw new VerificationCodeException("验证码错误.."); } } }
AuthenticationEntryPointImpl
/** * 认证失败处理类(jwt) 返回未授权 * */ @Component public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); //ReturnCode rc = ReturnCode.NO_OPERATOR_AUTH; ServletUtils.renderString(response, JSON.toJSONString(ResultData.error(403,msg))); } }
CustomAuthenticationEntryPoint
/** * 认证失败处理类(web) 返回未授权 * */ @Component @Slf4j public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { log.info("登录失败....11"); //authException.printStackTrace(); response.setContentType("text/html; charset=UTF-8"); request.setAttribute("errorMsg", "登录失败"); request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,authException); request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, authException); response.sendRedirect("/login?error=AuthenticationEntryPointImpl"); //request.getRequestDispatcher("/login?error=AuthenticationEntryPointImpl").forward(request, response); } }
LogoutSuccessHandlerImpl
/** * 自定义退出处理类 返回成功 * * @author ruoyi */ @Configuration public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { //要在配置文件配置 //@Autowired //private TokenService tokenService; /** * 退出处理 * * @return */ @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { // LoginUser loginUser = tokenService.getLoginUser(request); // if (StringUtils.isNotNull(loginUser)) // { // String userName = loginUser.getUsername(); // // 删除用户缓存记录 // tokenService.delLoginUser(loginUser.getToken()); // // 记录用户退出日志 // AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success"))); // } // ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success")))); } }
MyAccessDeniedHandler 无权限访问处理器:返回一些json或字符串
public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle( HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException ) throws IOException, ServletException { // ObjectMapper objectMapper = new ObjectMapper(); // CommonResult commonResult = new CommonResult(); // commonResult.setCode(YIXGResultEnum.NO_PERMISSION.getCode()) // .setMessage(YIXGResultEnum.NO_PERMISSION.getMessage()); // response.setContentType("application/json;charset=UTF-8"); // response.getWriter().write(objectMapper.writeValueAsString(commonResult)); // response.getWriter().flush(); // response.getWriter().close(); } }
4、用到的一些额外工具类,jwt工具类,字符串工具类,servlet等工具类。
JwtUtils :
/** * JWT工具类 */ public class JwtUtils { //有效期为 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=JwtUtils.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 jwt = createJWT("123"); System.out.println(jwt); Claims claims1 = parseJWT("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0NjBmYjNlZjkyZmU0OGU5YWI0YjlkNWE4ZjBmZmQ1OSIsInN1YiI6IjIiLCJpc3MiOiJzZyIsImlhdCI6MTY3NzU3MTIzNywiZXhwIjoxNjc3NTc0ODM3fQ.Tbt70u_ZSGECVIVUv2MhtdV2WnhR_Dxy47AlNWKBj4k"); System.out.println(claims1.getSubject()); // 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(JwtUtils.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(); } }
ServletUtils: 具体一些工具类 可以参照 ruoyi框架中的
/** * 客户端工具类 * * @author ruoyi */ public class ServletUtils { /** * 获取String参数 */ public static String getParameter(String name) { return getRequest().getParameter(name); } /** * 获取String参数 */ public static String getParameter(String name, String defaultValue) { return Convert.toStr(getRequest().getParameter(name), defaultValue); } /** * 获取Integer参数 */ public static Integer getParameterToInt(String name) { return Convert.toInt(getRequest().getParameter(name)); } /** * 获取Integer参数 */ public static Integer getParameterToInt(String name, Integer defaultValue) { return Convert.toInt(getRequest().getParameter(name), defaultValue); } /** * 获取Boolean参数 */ public static Boolean getParameterToBool(String name) { return Convert.toBool(getRequest().getParameter(name)); } /** * 获取Boolean参数 */ public static Boolean getParameterToBool(String name, Boolean defaultValue) { return Convert.toBool(getRequest().getParameter(name), defaultValue); } /** * 获得所有请求参数 * * @param request 请求对象{@link ServletRequest} * @return Map */ public static Map<String, String[]> getParams(ServletRequest request) { final Map<String, String[]> map = request.getParameterMap(); return Collections.unmodifiableMap(map); } /** * 获得所有请求参数 * * @param request 请求对象{@link ServletRequest} * @return Map */ public static Map<String, String> getParamMap(ServletRequest request) { Map<String, String> params = new HashMap<>(); for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) { params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); } return params; } /** * 获取request */ public static HttpServletRequest getRequest() { return getRequestAttributes().getRequest(); } /** * 获取response */ public static HttpServletResponse getResponse() { return getRequestAttributes().getResponse(); } /** * 获取session */ public static HttpSession getSession() { return getRequest().getSession(); } public static ServletRequestAttributes getRequestAttributes() { RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); return (ServletRequestAttributes) attributes; } /** * 将字符串渲染到客户端 * * @param response 渲染对象 * @param string 待渲染的字符串 */ public static void renderString(HttpServletResponse response, String string) throws IOException { try { response.setStatus(200); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().print(string); } catch (IOException e) { e.printStackTrace(); } } /** * 是否是Ajax异步请求 * * @param request */ public static boolean isAjaxRequest(HttpServletRequest request) { String accept = request.getHeader("accept"); if (accept != null && accept.contains("application/json")) { return true; } String xRequestedWith = request.getHeader("X-Requested-With"); if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { return true; } String uri = request.getRequestURI(); if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) { return true; } String ajax = request.getParameter("__ajax"); return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); } /** * 内容编码 * * @param str 内容 * @return 编码后的内容 */ public static String urlEncode(String str) { try { return URLEncoder.encode(str, Constants.UTF8); } catch (UnsupportedEncodingException e) { return StringUtils.EMPTY; } } /** * 内容解码 * * @param str 内容 * @return 解码后的内容 */ public static String urlDecode(String str) { try { return URLDecoder.decode(str, Constants.UTF8); } catch (UnsupportedEncodingException e) { return StringUtils.EMPTY; } } }
标签:return,String,request,认证,static,springboot3,new,security,public From: https://www.cnblogs.com/fps2tao/p/18243936