目录
4.3.2 登录过滤器 成功 获取token 保存redis
Spring Cloud Gateway是基于Spring Boot 2.x,Spring WebFlux和Project Reactor构建的。结果,当您使用Spring Cloud Gateway时,许多您熟悉的同步库(例如,Spring Data和Spring Security)和模式可能不适用。如果您不熟悉这些项目,建议您在使用Spring Cloud Gateway之前先阅读它们的文档以熟悉一些新概念。
Spring Security是一个提供身份验证,授权和保护以防止常见攻击的框架。凭借对命令式和响应式应用程序的一流支持,它是用于保护基于Spring的应用程序的事实上的标准。
Spring框架中包含的原始Web框架Spring Web MVC是专门为Servlet API和Servlet容器而构建的。响应式堆栈Web框架Spring WebFlux在稍后的5.0版中添加。它是完全无阻塞的,支持 Reactive Streams背压,并在Netty,Undertow和Servlet 3.1+容器等服务器上运行。
这两个Web框架都反映了其源模块的名称(spring-webmvc和 spring-webflux),并在Spring Framework中并存。每个模块都是可选的。应用程序可以使用一个模块,也可以使用两个模块,在某些情况下,也可以使用两个模块,例如,带有react的Spring MVC控制器WebClient。
注意由于Web容器不同,在Gateway项目中使用的WebFlux,是不能和Spring-Web混合使用的。 Spring MVC和 WebFlux 的区别:

dependencies {
implementation(
'org.springframework.cloud:spring-cloud-starter-gateway',
'org.springframework.boot:spring-boot-starter-security'
)
}
复制代码
spring security设置要采用响应式配置,基于WebFlux中WebFilter实现,与Spring MVC的Security是通过Servlet的Filter实现类似,也是一系列filter组成的过滤链。
Reactor与传统MVC配置对应:
| webflux | mvc | 作用 |
|---|---|---|
| @EnableWebFluxSecurity | @EnableWebSecurity | 开启security配置 |
| ServerAuthenticationSuccessHandler | AuthenticationSuccessHandler | 登录成功Handler |
| ServerAuthenticationFailureHandler | AuthenticationFailureHandler | 登陆失败Handler |
| ReactiveAuthorizationManager | AuthorizationManager | 认证管理 |
| ServerSecurityContextRepository | SecurityContextHolder | 认证信息存储管理 |
| ReactiveUserDetailsService | UserDetailsService | 用户登录 |
| ReactiveAuthorizationManager | AccessDecisionManager | 鉴权管理 |
| ServerAuthenticationEntryPoint | AuthenticationEntryPoint | 未认证Handler |
| ServerAccessDeniedHandler | AccessDeniedHandler | 鉴权失败Handler |
package com.pluto.gateway.security;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.LinkedList;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/11 10:56
* @description webflux security核心配置类
*/
@EnableWebFluxSecurity
public class WebfluxSecurityConfig {
@Resource
private DefaultAuthorizationManager defaultAuthorizationManager;
@Resource
private UserDetailsServiceImpl userDetailsServiceImpl;
@Resource
private DefaultAuthenticationSuccessHandler defaultAuthenticationSuccessHandler;
@Resource
private DefaultAuthenticationFailureHandler defaultAuthenticationFailureHandler;
@Resource
private TokenAuthenticationManager tokenAuthenticationManager;
@Resource
private DefaultSecurityContextRepository defaultSecurityContextRepository;
@Resource
private DefaultAuthenticationEntryPoint defaultAuthenticationEntryPoint;
@Resource
private DefaultAccessDeniedHandler defaultAccessDeniedHandler;
/**
* 自定义过滤权限
*/
@Value("${security.noFilter}")
private String noFilter;
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity) {
httpSecurity
// 登录认证处理
.authenticationManager(reactiveAuthenticationManager())
.securityContextRepository(defaultSecurityContextRepository)
// 请求拦截处理
.authorizeExchange(exchange -> exchange
.pathMatchers(noFilter).permitAll()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.anyExchange().access(defaultAuthorizationManager)
)
.formLogin()
// 自定义处理
.authenticationSuccessHandler(defaultAuthenticationSuccessHandler)
.authenticationFailureHandler(defaultAuthenticationFailureHandler)
.and()
.exceptionHandling()
.authenticationEntryPoint(defaultAuthenticationEntryPoint)
.and()
.exceptionHandling()
.accessDeniedHandler(defaultAccessDeniedHandler)
.and()
.csrf().disable()
;
return httpSecurity.build();
}
/**
* BCrypt密码编码
*/
@Bean("passwordEncoder")
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
/**
* 注册用户信息验证管理器,可按需求添加多个按顺序执行
*/
@Bean
ReactiveAuthenticationManager reactiveAuthenticationManager() {
LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
managers.add(authentication -> {
// 其他登陆方式 (比如手机号验证码登陆) 可在此设置不得抛出异常或者 Mono.error
return Mono.empty();
});
// 必须放最后不然会优先使用用户名密码校验但是用户名密码不对时此 AuthenticationManager 会调用 Mono.error 造成后面的 AuthenticationManager 不生效
managers.add(new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsServiceImpl));
managers.add(tokenAuthenticationManager);
return new DelegatingReactiveAuthenticationManager(managers);
}
}
复制代码
package com.pluto.gateway.security;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.io.Serializable;
import java.util.Collection;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/10 13:15
* @description 自定义用户信息
*/
public class SecurityUserDetails extends User implements Serializable {
private Long userId;
public SecurityUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities, Long userId) {
super(username, password, authorities);
this.userId = userId;
}
public SecurityUserDetails(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities, Long userId) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.userId = userId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
}
复制代码
package com.pluto.gateway.security;
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.ArrayList;
/**
* @author ceshi
* @date 2021/3/9 14:03
* @description 用户登录处理
* @version 1.0.0
*/@Service
public class UserDetailsServiceImpl implements ReactiveUserDetailsService {
@Resource
private PasswordEncoder passwordEncoder;
@Override
public Mono<UserDetails> findByUsername(String username) {
SecurityUserDetails securityUserDetails = new SecurityUserDetails(
"user",
passwordEncoder.encode("user"),
true, true, true, true, new ArrayList<>(),
1L
);
return Mono.just(securityUserDetails);
}
}
复制代码
package com.pluto.gateway.security;
import com.alibaba.fastjson.JSONObject;
import com.pluto.common.basic.utils.JwtTokenUtil;
import com.pluto.common.basic.utils.ResultVoUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/11 15:00
* @description 登录成功处理
*/
@Component
public class DefaultAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {
/**
* token 过期时间
*/
@Value("${jwt.token.expired}")
private int jwtTokenExpired;
/**
* 刷新token 时间
*/
@Value("${jwt.token.refresh.expired}")
private int jwtTokenRefreshExpired;
@Override
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
return Mono.defer(() -> Mono.just(webFilterExchange.getExchange().getResponse()).flatMap(response -> {
DataBufferFactory dataBufferFactory = response.bufferFactory();
// 生成JWT token
Map<String, Object> map = new HashMap<>(2);
SecurityUserDetails userDetails = (SecurityUserDetails) authentication.getPrincipal();
map.put("userId", userDetails.getUserId());
map.put("username", userDetails.getUsername());
map.put("roles",userDetails.getAuthorities());
String token = JwtTokenUtil.generateToken(map, userDetails.getUsername(), jwtTokenExpired);
String refreshToken = JwtTokenUtil.generateToken(map, userDetails.getUsername(), jwtTokenRefreshExpired);
Map<String, Object> tokenMap = new HashMap<>(2);
tokenMap.put("token", token);
tokenMap.put("refreshToken", refreshToken);
DataBuffer dataBuffer = dataBufferFactory.wrap(JSONObject.toJSONString(ResultVoUtil.success(tokenMap)).getBytes());
return response.writeWith(Mono.just(dataBuffer));
}));
}
}
复制代码
package com.pluto.gateway.security;
import com.alibaba.fastjson.JSONObject;
import com.pluto.common.basic.enums.UserStatusCodeEnum;
import com.pluto.common.basic.utils.ResultVoUtil;
import com.pluto.common.basic.vo.ResultVO;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.Map;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/11 15:14
* @description 登录失败处理
*/
@Component
public class DefaultAuthenticationFailureHandler implements ServerAuthenticationFailureHandler {
@Override
public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
return Mono.defer(() -> Mono.just(webFilterExchange.getExchange()
.getResponse()).flatMap(response -> {
DataBufferFactory dataBufferFactory = response.bufferFactory();
ResultVO<Map<String, Object>> resultVO = ResultVoUtil.error();
// 账号不存在
if (exception instanceof UsernameNotFoundException) {
resultVO = ResultVoUtil.failed(UserStatusCodeEnum.ACCOUNT_NOT_EXIST);
// 用户名或密码错误
} else if (exception instanceof BadCredentialsException) {
resultVO = ResultVoUtil.failed(UserStatusCodeEnum.LOGIN_PASSWORD_ERROR);
// 账号已过期
} else if (exception instanceof AccountExpiredException) {
resultVO = ResultVoUtil.failed(UserStatusCodeEnum.ACCOUNT_EXPIRED);
// 账号已被锁定
} else if (exception instanceof LockedException) {
resultVO = ResultVoUtil.failed(UserStatusCodeEnum.ACCOUNT_LOCKED);
// 用户凭证已失效
} else if (exception instanceof CredentialsExpiredException) {
resultVO = ResultVoUtil.failed(UserStatusCodeEnum.ACCOUNT_CREDENTIAL_EXPIRED);
// 账号已被禁用
} else if (exception instanceof DisabledException) {
resultVO = ResultVoUtil.failed(UserStatusCodeEnum.ACCOUNT_DISABLE);
}
DataBuffer dataBuffer = dataBufferFactory.wrap(JSONObject.toJSONString(resultVO).getBytes());
return response.writeWith(Mono.just(dataBuffer));
}));
}
}
复制代码
package com.pluto.gateway.security;
import com.alibaba.fastjson.JSONObject;
import com.pluto.common.basic.enums.UserStatusCodeEnum;
import com.pluto.common.basic.utils.ResultVoUtil;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.Charset;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/11 15:17
* @description 未认证处理
*/
@Component
public class DefaultAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
@Override
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException ex) {
return Mono.defer(() -> Mono.just(exchange.getResponse())).flatMap(response -> {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
DataBufferFactory dataBufferFactory = response.bufferFactory();
String result = JSONObject.toJSONString(ResultVoUtil.failed(UserStatusCodeEnum.USER_UNAUTHORIZED));
DataBuffer buffer = dataBufferFactory.wrap(result.getBytes(
Charset.defaultCharset()));
return response.writeWith(Mono.just(buffer));
});
}
}
复制代码
package com.pluto.gateway.security;
import com.alibaba.fastjson.JSONObject;
import com.pluto.common.basic.enums.UserStatusCodeEnum;
import com.pluto.common.basic.utils.ResultVoUtil;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.Charset;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/11 11:12
* @description 鉴权管理
*/
@Component
public class DefaultAccessDeniedHandler implements ServerAccessDeniedHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {
return Mono.defer(() -> Mono.just(exchange.getResponse()))
.flatMap(response -> {
response.setStatusCode(HttpStatus.OK);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
DataBufferFactory dataBufferFactory = response.bufferFactory();
String result = JSONObject.toJSONString(ResultVoUtil.failed(UserStatusCodeEnum.PERMISSION_DENIED));
DataBuffer buffer = dataBufferFactory.wrap(result.getBytes(
Charset.defaultCharset()));
return response.writeWith(Mono.just(buffer));
});
}
}
复制代码
package com.pluto.gateway.security;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.List;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/11 16:27
* @description 存储认证授权的相关信息
*/
@Component
public class DefaultSecurityContextRepository implements ServerSecurityContextRepository {
public final static String TOKEN_HEADER = "Authorization";
public final static String BEARER = "Bearer ";
@Resource
private TokenAuthenticationManager tokenAuthenticationManager;
@Override
public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
return Mono.empty();
}
@Override
public Mono<SecurityContext> load(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
List<String> headers = request.getHeaders().get(TOKEN_HEADER);
if (!CollectionUtils.isEmpty(headers)) {
String authorization = headers.get(0);
if (StringUtils.isNotEmpty(authorization)) {
String token = authorization.substring(BEARER.length());
if (StringUtils.isNotEmpty(token)) {
return tokenAuthenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(token, null)
).map(SecurityContextImpl::new);
}
}
}
return Mono.empty();
}
}
复制代码
package com.pluto.gateway.security;
import com.pluto.common.basic.utils.JwtTokenUtil;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.Collection;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/11 13:23
* @description token 认证处理
*/
@Component
@Primary
public class TokenAuthenticationManager implements ReactiveAuthenticationManager {
@Override
@SuppressWarnings("unchecked")
public Mono<Authentication> authenticate(Authentication authentication) {
return Mono.just(authentication)
.map(auth -> JwtTokenUtil.parseJwtRsa256(auth.getPrincipal().toString()))
.map(claims -> {
Collection<? extends GrantedAuthority> roles = (Collection<? extends GrantedAuthority>) claims.get("roles");
return new UsernamePasswordAuthenticationToken(
claims.getSubject(),
null,
roles
);
});
}
}
复制代码
package com.pluto.gateway.security;
import com.alibaba.fastjson.JSONObject;
import com.pluto.common.basic.enums.UserStatusCodeEnum;
import com.pluto.common.basic.utils.ResultVoUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Collection;
/**
* @author ShiLei
* @version 1.0.0
* @date 2021/3/11 13:10
* @description 用户权限鉴权处理
*/
@Component
@Slf4j
public class DefaultAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
return authentication.map(auth -> {
ServerWebExchange exchange = authorizationContext.getExchange();
ServerHttpRequest request = exchange.getRequest();
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
for (GrantedAuthority authority : authorities) {
String authorityAuthority = authority.getAuthority();
String path = request.getURI().getPath();
// TODO
// 查询用户访问所需角色进行对比
if (antPathMatcher.match(authorityAuthority, path)) {
log.info(String.format("用户请求API校验通过,GrantedAuthority:{%s} Path:{%s} ", authorityAuthority, path));
return new AuthorizationDecision(true);
}
}
return new AuthorizationDecision(false);
}).defaultIfEmpty(new AuthorizationDecision(false));
}
@Override
public Mono<Void> verify(Mono<Authentication> authentication, AuthorizationContext object) {
return check(authentication, object)
.filter(AuthorizationDecision::isGranted)
.switchIfEmpty(Mono.defer(() -> {
String body = JSONObject.toJSONString(ResultVoUtil.failed(UserStatusCodeEnum.PERMISSION_DENIED));
return Mono.error(new AccessDeniedException(body));
})).flatMap(d -> Mono.empty());
}
}
复制代码
下面看看是哪里进行的密码比较
1 /spring-security-core-5.1.4.RELEASE-sources.jar!/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
......
try {
preAuthenticationChecks.check(user);
// 重点看 additionalAuthenticationChecks 密码判断
additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);
}
catch (AuthenticationException exception) {
if (cacheWasUsed) {
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed = false;
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
else {
throw exception;
}
}
postAuthenticationChecks.check(user);
......
return createSuccessAuthentication(principalToReturn, authentication, user);
}
2 /spring-security-core-5.1.4.RELEASE-sources.jar!/org/springframework/security/authentication/dao/DaoAuthenticationProvider.java
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
// 密码比较就在这个地方,前面这个是用户输入的密码,后面这个是数据库存的密码,一致则通过
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}










用户信息 还可以存放权限信息




















3.3.3





3.5注解访问









密码

package com.atguigu.serurity.config;
import com.atguigu.serurity.filter.TokenAuthenticationFilter;
import com.atguigu.serurity.filter.TokenLoginFilter;
import com.atguigu.serurity.security.DefaultPasswordEncoder;
import com.atguigu.serurity.security.TokenLogoutHandler;
import com.atguigu.serurity.security.TokenManager;
import com.atguigu.serurity.security.UnauthorizedEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
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.core.userdetails.UserDetailsService;
/**
* <p>
* Security配置类
* </p>
*
* @author qy
* @since 2019-11-18
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;//用户详情服务
private TokenManager tokenManager;//令牌管理器
private DefaultPasswordEncoder defaultPasswordEncoder;//默认密码编码器
private RedisTemplate redisTemplate;//Redis 模板
//令牌网络安全配置
@Autowired
public TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,
TokenManager tokenManager, RedisTemplate redisTemplate) {
this.userDetailsService = userDetailsService;
this.defaultPasswordEncoder = defaultPasswordEncoder;
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
}
/**
* 配置设置
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("configure(HttpSecurity http)"+http);
http.exceptionHandling()
.authenticationEntryPoint(new UnauthorizedEntryPoint())
.and().csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().logout().logoutUrl("/admin/acl/index/logout")
.addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and()
.addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
.addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();
}
/**
* 密码处理
* @param auth
* @throws Exception
* 身份验证管理器生成器
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
}
/**
* 配置哪些请求不拦截
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/**",
"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"
);
}
}
package com.atguigu.serurity.entity;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* <p>
* 安全认证用户详情信息
* </p>
*
* @author qy
* @since 2019-11-08
*/
@Data
@Slf4j
public class SecurityUser implements UserDetails {
//当前登录用户
private transient User currentUserInfo;
//当前权限
private List<String> permissionValueList;
public SecurityUser() {
}
public SecurityUser(User user) {
if (user != null) {
this.currentUserInfo = user;
}
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
for(String permissionValue : permissionValueList) {
if(StringUtils.isEmpty(permissionValue)) continue;
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
authorities.add(authority);
}
return authorities;
}
@Override
public String getPassword() {
return currentUserInfo.getPassword();
}
@Override
public String getUsername() {
return currentUserInfo.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;
}
}
package com.atguigu.serurity.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
* 用户实体类
* </p>
*
* @author qy
* @since 2019-11-08
*/
@Data
@ApiModel(description = "用户实体类")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "微信openid")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "昵称")
private String nickName;
@ApiModelProperty(value = "用户头像")
private String salt;
@ApiModelProperty(value = "用户签名")
private String token;
}
package com.atguigu.serurity.filter;
import com.atguigu.commonutils.R;
import com.atguigu.commonutils.ResponseUtil;
import com.atguigu.serurity.security.TokenManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
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.Collection;
import java.util.List;
/**
* <p>
* 访问过滤器
* </p>
*
* @author qy
* @since 2019-11-08
*/
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenAuthenticationFilter(AuthenticationManager authManager, TokenManager tokenManager,RedisTemplate redisTemplate) {
super(authManager);
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
}
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws IOException, ServletException {
logger.info("=======TokenAuthenticationFilter-doFilterInternal=========="+req.getRequestURI());
System.out.println("req.getRequestURI().indexOf(\"admin\")"+req.getRequestURI().indexOf("admin"));
System.out.println("doFilterInternal-req"+req);
System.out.println("doFilterInternal-req.getRequestURI()"+req.getRequestURI());
// if(req.getRequestURI().indexOf("admin") == 1) {
// chain.doFilter(req, res);
// return;
// }
UsernamePasswordAuthenticationToken authentication = null;
try {
authentication = getAuthentication(req);
System.out.println("doFilterInternal-authentication"+authentication);
} catch (Exception e) {
ResponseUtil.out(res, R.error());
}
if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
ResponseUtil.out(res, R.error());
}
/*将请求转发给过滤器链上下一个对象。这里的下一个指的是下一个filter,
如果没有filter那就是你请求的资源。 一般filter都是一个链,web.xml 里面配置了几个就有几个。
一个一个的连在一起 request -> filter1 -> filter2 ->filter3 -> .... -> request resource.
*/
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
// token置于header里
String token = request.getHeader("token");
if (token != null && !"".equals(token.trim())) {
String userName = tokenManager.getUserFromToken(token);
List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);
Collection<GrantedAuthority> authorities = new ArrayList<>();
for(String permissionValue : permissionValueList) {
if(StringUtils.isEmpty(permissionValue)) continue;
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
authorities.add(authority);
}
if (!StringUtils.isEmpty(userName)) {
return new UsernamePasswordAuthenticationToken(userName, token, authorities);
}
return null;
}
return null;
}
}
package com.atguigu.serurity.filter;
import com.atguigu.commonutils.R;
import com.atguigu.commonutils.ResponseUtil;
import com.atguigu.serurity.entity.SecurityUser;
import com.atguigu.serurity.entity.User;
import com.atguigu.serurity.security.TokenManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.ArrayList;
/**
* <p>
* 登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验
* </p>
*
* @author qy
* @since 2019-11-08
*/
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {
this.authenticationManager = authenticationManager;
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
this.setPostOnly(false);
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
throws AuthenticationException {
try {
User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 登录成功
* @param req
* @param res
* @param chain
* @param auth
* @throws IOException
* @throws ServletException
*/
@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,
Authentication auth) throws IOException, ServletException {
SecurityUser user = (SecurityUser) auth.getPrincipal();
String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());
ResponseUtil.out(res, R.ok().data("token", token));
}
/**
* 登录失败
* @param request
* @param response
* @param e
* @throws IOException
* @throws ServletException
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
ResponseUtil.out(response, R.error());
}
}
package com.atguigu.serurity.security;
import com.atguigu.commonutils.MD5;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
* <p>
* t密码的处理方法类型
* </p>
*
* @author qy
* @since 2019-11-08
*/
@Component
public class DefaultPasswordEncoder implements PasswordEncoder {
public DefaultPasswordEncoder() {
this(-1);
}
/**
* @param strength
* the log rounds to use, between 4 and 31
*/
public DefaultPasswordEncoder(int strength) {
}
/*
* 密码加密
* */
public String encode(CharSequence rawPassword) {
return MD5.encrypt(rawPassword.toString());
}
/*
* 密码是否相等
* */
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));
}
}
package com.atguigu.serurity.security;
import com.atguigu.commonutils.R;
import com.atguigu.commonutils.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <p>
* 登出业务逻辑类
* </p>
*
* @author qy
* @since 2019-11-08
*/
public class TokenLogoutHandler implements LogoutHandler {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
}
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
String token = request.getHeader("token");
if (token != null) {
tokenManager.removeToken(token);
//清空当前用户缓存中的权限数据
String userName = tokenManager.getUserFromToken(token);
redisTemplate.delete(userName);
}
ResponseUtil.out(response, R.ok());
}
}
package com.atguigu.serurity.security;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* <p>
* token管理
* </p>
*
* @author qy
* @since 2019-11-08
*/
@Component
public class TokenManager {
private long tokenExpiration = 24*60*60*1000;
private String tokenSignKey = "123456";
public String createToken(String username) {
String token = Jwts.builder().setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
.signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();
return token;
}
public String getUserFromToken(String token) {
String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();
return user;
}
public void removeToken(String token) {
//jwttoken无需删除,客户端扔掉即可。
}
}
package com.atguigu.serurity.security;
import com.atguigu.commonutils.R;
import com.atguigu.commonutils.ResponseUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* <p>
* 未授权的统一处理方式
* </p>
*
* @author qy
* @since 2019-11-08
*/
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
ResponseUtil.out(response, R.error());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>commont</artifactId>
<groupId>com.atguigu</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring_security</artifactId>
<dependencies>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>comment_utils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Security依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>guli_parent</artifactId>
<groupId>com.atguigu</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>commont</artifactId>
<packaging>pom</packaging>
<modules>
<module>service_base</module>
<module>comment_utils</module>
<module>spring_security</module>
</modules>
<dependencies>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided </scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<scope>provided </scope>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided </scope>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<scope>provided </scope>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>-->
</dependencies>
</project>
package com.atguigu.commonutils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @author helen
* @since 2019/10/16
* 生成字符串,包含用户信息,
* jwt生成包含三部分:1头2.有效荷载3.签名哈希 防伪标志
*/
public class JwtUtils {
public static final long EXPIRE = 1000 * 60 * 60 * 24;//token过期时间
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";//密钥
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
//第一步分
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//第二部分
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
//第三部分 token主体
.claim("id", id)
.claim("nickname", nickname)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param jwtToken
* @return
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request
* @return
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
System.out.println("jwtToken"+jwtToken);
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken))
return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
System.out.println("claims:"+claims);
return (String)claims.get("id");
}
}
package com.atguigu.commonutils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class MD5 {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
System.out.println("bytes"+bytes);
int j = bytes.length;
System.out.println("j"+j);
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
System.out.println(" bytes[i]"+ bytes[i]);
//转化hexChars
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}
public static void main(String[] args) {
System.out.println(MD5.encrypt("111111"));
}
}
package com.atguigu.commonutils;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class R {
@ApiModelProperty(value = "是否成功")
private Boolean success;
@ApiModelProperty(value = "返回码")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String message;
@ApiModelProperty(value = "返回数据")
private Map<String, Object> data = new HashMap<String, Object>();
//构造器私有化
private R() {
}
//成功静态方法
public static R ok() {
R r = new R();
r.setSuccess(true);
r.setCode(ResultCode.SUCCESS);
r.setMessage("成功");
return r;
}
//失败静态方法
public static R error() {
R r = new R();
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
r.setMessage("失败");
return r;
}
public R success(Boolean success) {
this.setSuccess(success);
return this;
}
public R message(String message) {
this.setMessage(message);
return this;
}
public R code(Integer code) {
this.setCode(code);
return this;
}
public R data(String key, Object value) {
this.data.put(key, value);
return this;
}
public R data(Map<String, Object> map) {
this.setData(map);
return this;
}
}
我正在尝试找到一种更好的方法将IRB与我的常规ruby开发集成。目前我很少在我的代码中使用IRB。我只用它来验证语法或尝试一些小的东西。我知道我可以将我自己的代码加载到ruby中作为一个require'mycode'但这通常不符合我的编程风格。有时我要检查的变量超出范围或在循环内。有没有一种简单的方法可以启动我的脚本并在IRB内的某个点卡住?我想我正在寻找一种更简单的方法来调试我的ruby代码而不破坏我的F5(编译)键。也许有经验的ruby开发者可以和我分享一个更精简的开发方法。 最佳答案 安装ruby-debugg
我开始了一个小型网络项目并使用Drupal来构建它。到目前为止,还不错:您可以快速建立一个不错的面向CMS的网站,通过模块添加社交功能,并且您有一个广泛的API可以在一个架构良好的平台中进行自定义。现在问题来了:网站的增长超出了最初的计划,我发现自己正处于认真开始为它编写代码的境地。由于Drupal项目,我对PHP有了新的认识,但我想用Ruby来做。我会感觉更舒服,以后维护起来更容易,我可以在其他Ruby/Rails应用程序中重用它。随着时间的推移,我想我会用Ruby重写Drupal中的现有部分。基于此,问题是:是否有人将两者(成功或失败的故事)结合起来?这是一个相当大的决定,但我在G
为什么需要服务网关传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关,客户端只能在本地记录每个微服务的调用地址,当需要调用的微服务数量很多时,它需要了解每个服务的接口,这个工作量很大。有了网关之后,网关作为系统的唯一流量入口,封装内部系统的架构,所有请求都先经过网关,由网关将请求路由到合适的微服务。使用网关的好处1)简化客户端的工作。网关将微服务封装起来后,客户端只需同网关交互,而不必调用各个不同服务;(2)降低函数间的耦合度。一旦服务接口修改,只需修改网关的路由策略,不必修改每个调用该函数的客户端,从而减少了程序间的耦合性(3)解放开发
我需要部署我的Rails应用程序,所以我从这里开始执行了所有步骤,https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-puma-and-nginx-on-ubuntu-14-04但是在教程结束时,我得到了这个错误-->“502BadGateway”编辑现在的错误消息-->“很抱歉,出了点问题。”但是Nginx错误输出是相同的,我检查了puma错误消息,但它们只是记录它何时启动以及何时正常停止。位于app_directory/log下的Rails日志不产生任何输出。puma-man
一、Elasticsearch简介实际业务场景中,多端的查询功能都有很大的优化空间。常见的处理方式有:建索引、建物化视图简化查询逻辑、DB层之上建立缓存、分页…然而随着业务数据量的不断增多,总有那么一张表或一个业务,是无法通过常规的处理方式来缩短查询时间的。在查询功能优化上,作为开发人员应该站在公司的角度,本着优化客户体验的目的去寻找解决方案。本人有幸做过Tomcat整合solr,今天一起研究一下当前比较火热的Elasticsearch搜索引擎。Elasticsearch是一个非常强大的搜索引擎。它目前被广泛地使用于各个IT公司。Elasticsearch是由Elastic公司创建。它的代码位
本篇博文目录:一.SpringSecurity简介1.SpringSecurity2.SpringSecurity相关概念二.认证和授权1.认证(1)使用SpringSecurity进行简单的认证(SpringBoot项目中)(2)SpringSecurity的原理(3)SpringSecurity核心类(4)认证登入案例(JWT+SpringSecurity实现登入案例)2.授权(1)加入权限到Authentication中(2)SecurityConfig配置文件中开启注解权限配置(3)给接口中的方法添加访问权限(4)用户权限表的建立3.自定义失败处理(1)创建异常处理类(2)配置移除处理
目录springSecurity授权权限管理策略基于url的权限管理基于方法的权限管理将url权限管理设为动态会话管理会话并发管理会话失效处理禁止再次登录会话共享源码分析CSRF跨站请求伪造开启CSRF防御传统web开发前后端分离开启CSRF防护csrf防御过程CORS跨域问题springBoot解决跨域的三种方式springSecurity解决跨域springSecurity授权认证与授权解耦授权:据系统提前设置好的规则,给用户分配可以访问某一个资源的权限,用户根据自己所具有权限,去执行相应操作。GrantedAuthority应该如何理解呢?是角色还是权限?权限是具体一些操作,角色是一些权
Easy-Es(简称EE)是一款基于ElasticSearch(简称Es)官方提供的RestHighLevelClient打造的ORM开发框架,在RestHighLevelClient的基础上,只做增强不做改变,为简化开发、提高效率而生,您如果有用过Mybatis-Plus(简称MP),那么您基本可以零学习成本直接上手EE,EE是MP的Es平替版,在有些方面甚至比MP更简单,同时也融入了更多Es独有的功能,助力您快速实现各种场景的开发。目录1、ES的优点2、整合过程(1)配置文件(2
之前跟着老师的视频安装了一个es6.4.3,视频里说“6->7类型逐渐被废弃,版本变化较大,与springboot不兼容,建议安装6.4.3与内部一致”,我就很天真的跟着一块装了,结果可想而知……我的springboot版本是2.6.4啊,怎么可能不报错呢??!还是要多看官方文件!整合最重要的就是这张图,版本对了什么都好说es对应的springboot版本SpringDataReleaseTrainSpringDataElasticsearchElasticsearchSpringFrameworkSpringBoot2021.2(Raj)4.4.x7.17.45.3.x2.7.x2021.1
文章目录概述认证认证插件基于静态token的认证服务实践基于X509证书认证实践基于webhook认证实践鉴权k8s中RBAC的使用授权实践准入场景配额管理实践插件插件开发限流APIPriorityandFairnessAPF中的排队FlowSchema与PriorityLevelConfiguration(队列权重配置)调试命令概述kube-apiserver是k8s最重要的控制组件之一,主要提供以下功能:提供集群管理的RESTAPI接口,包括认证授权、数据校验以及集群状态变更等k8s中所有模块与etcd的数据交互都需要走APIServer,禁止直接和etcd通信APIServer请求流程概