spring安全框架,java的安全框架是什么
spring安全框架,java的安全框架是什么详细介绍
本文目录一览: java的安全框架是什么
java的安全框架是什么?让我们一起看看吧!JAVA框架就是一些类和接口的集合,通过这些类和接口协调来完成一系列的程序实现,Java安全框架是指在用户使用过程中能够保证程序安全性的框架。下面介绍两种常见的Java安全框架:1、SpringSecurity:是一个强大且高度可定制的安全框架,致力于为Java应用提供身份认证和授权,核心功能主要包括:认证、授权和攻击防护 。2、Apache Shiro是一个强大且易用的Java安全框架,核心功能主要包括:执行身份验证、授权、密码学和会话管理。今天的分享就是这些,希望能给大家帮助哟!
Spring Security基础原理
Spring Security:是一个提供身份验证,授权和保护以防止常见攻击的框架。 凭借对命令式和反应式应用程序的一流支持,它为Spring应用程序的安全提供实际标准。
Spring Security为身份验证,授权和针对常见漏洞的防护提供了全面的支持。 它还集成了三方库,以简化其使用。
Spring Security为身份验证提供了全面的支持。 身份验证是我们验证谁试图访问特定资源的身份的方法。 验证用户身份的常用方法是要求用户输入用户名和密码。 一旦执行了身份验证,我们就会知道身份并可以执行授权。
Spring Security提供了用于验证用户的内置支持。 分为两大主体:基于Servlet和WebFlux的身份验证。
Spring Security的PasswordEncoder接口用于对密码进行单向转换,以使密码可以安全地存储。
基于PasswordEncoder接口的实现类如下:
Spring提供了一个名为DelegatingFilterProxy的Filter实现,可以在Servlet容器的生命周期和Spring的ApplicationContext之间进行桥接。 Servlet容器允许使用自己的标准注册Filters,但是它不知道Spring定义的Bean。 DelegatingFilterProxy可以通过标准的Servlet容器机制进行注册,然后将所有工作委托给实现Filter的Spring Bean。
如上图所示,DelegatingFilterProxy是一个标准的Servlet Filter,当调用链路到DelegatingFilterProxy,DelegatingFilterProxy会找到达Spring管理的Filter,然后发起调用。
如上图所示,FilterChainProxy是Spring Security提供的特殊过滤器,允许通过SecurityFilterChain委派许多过滤器实例。
如上图所示,FilterChainProxy使用SecurityFilterChain确定应对此请求调用哪些Spring Security过滤器。
Security Filter是注册到FilterChainProxy而不是DelegatingFilterProxy的。原因如下:
Spring Security提供了以下Security Filter(包含顺序,通过FilterComparator配置相关顺序):
如上图表示的是异常处理过滤器ExceptionTranslationFilter的工作原理:
如上图所示,ProviderManager作为AuthenticationManager最常见的实现,ProviderManager认证时,将认证逻辑委托给AuthenticationProvider列表,不同AuthenticationProvider的执行不同的认证逻辑。
如果没有AuthenticationProvider可以执行身份验证,使用该父AuthenticationManager(通常是ProviderManager实例)进行认证。
一个父AuthenticationManager可能存在多个ProviderManager实例。 即具有相同身份验证(共享父AuthenticationManager)但又具有不同身份验证机制(不同ProviderManager实例)。
验证用户身份的最常见方法之一是验证用户名和密码, Spring Security为使用用户名和密码进行身份验证提供了全面的支持。
上图表示用户在表单提交用户名、密码的验证流程:
上图表示当客户端收到WWW-Authenticate响应头时,使用用户名和密码登录的流程:
Remember-Me(记住我),主要用于在一段很长的时间内(通常15天),用户只需要登录一次,就无需再登录了(前提是用户名、密码、秘钥不变的情况)。
原理:当用户登录成功时,服务端会向浏览器额外发送一个cookie(name = remember-me, value = token值),之后的请求都会携带这个cookie,当用户session失效时(比如2小时过后),该cookie携带到服务端触发自动登录。
当然,Remember-Me会存在一些安全问题,Remember-Me的token可以被用户代理捕获到,可以轻松通过该token去修改密码。因此在一些安全性重要的应用上面,不建议开启Remember-Me。
FilterSecurityInterceptor为HttpServletRequests提供授权,它作为安全筛选器之一插入到FilterChainProxy中(除此之外,Spring Security支持服务层方法授权还有域对象授权)。
回顾下前面学到的知识,用户登录认证成功后,会为当前用户生成一个Authentication对象,该对象包含了Principal、Credentials和Authorities;
该Authorities由GrantedAuthority(默认实现:SimpleGrantedAuthority)集合组成,GrantedAuthority接口只有一个方法,如下:
一般情况下GrantedAuthority由String表示,如果GrantedAuthority无法精确地表示为String,则GrantedAuthority被视为“复杂”,并且getAuthority()必须返回null。
刚刚讲到Spring Security调用AbstractSecurityInterceptor.beforeInvocation()进入前置处理阶段,该阶段的一个重点就是进行访问决策处理,由AccessDecisionManager相关实现来完成,AccessDecisionManager接口包含三个方法:
如上图所示,Spring Security提供了三个决策处理器AccessDecisionManager的实现类(AffirmativeBased、ConsensusBased、UnanimousBased),代表三种不同的决策处理器,当然也可以自定义决策处理器。决策处理器将决策逻辑委托给多个投票器AccessDecisionVoter(具体实现有:AuthenticatedVoter、RoleVoter、WebExpressionVoter等),接着AccessDecisionManager将投票结果进行整合,返回拒绝或者成功。
AccessDecisionManager实现类的具体描述如下:
AccessDecisionVoter通过返回int来表示投票的结果,有ACCESS_ABSTAIN(0,弃权),ACCESS_DENIED(-1,不通过)和ACCESS_GRANTED(1,通过),AccessDecisionVoter主要的实现类如下:
某些应用程序需要一种修改返回的对象的方法,因此Spring Security提供了一个方便的挂钩AfterInvocationManager,通过AfterInvocationManager来修改返回对象。
如上图所示,AfterInvocationManager有一个具体的实现AfterInvocationProviderManager,它轮询AfterInvocationProvider的列表。 每个AfterInvocationProvider都可以修改返回对象或引发AccessDeniedException。 实际上,由于前一个提供程序的结果将传递到列表中的下一个,因此多个提供程序可以修改该对象。
真牛掰!阿里人用5个案例就彻底讲清了SpringSecurity安全框架
安全框架顾名思义,就是 解决系统安全问题的框架 。任何应用开发的计划阶段都应该确定一组特定的安全需求,如身份验证、授权和加密方式。不使用安全框架之前,我们需要手动处理每个资源的访问控制,针对不同的项目都需要做不同的处理,此时就会显得非常麻烦,并且低效率引起的额外开销会延缓开发周期。使用安全框架,使开发团队能够选择最适合这些需求的框架,可以通过配置的方式实现对资源的访问限制,使得开发更加的高效。
而对我们Java程序员来说,接触最多的安全框架之一应该就是Spring Security了,但是很多粉丝后台跟我反馈说, 工作上要用到的时候,到各大网站查阅资料博客,对此的描述很少,市面上含金量高点的相关资料更是难寻 。所以本着 好东西就是要拿出来的分享的原则 ,今天楼主正好借此机会把前段时间从阿里的朋友手上拿到的一套Spring Security实战全彩笔记分享给大家,看看是否能对大家有所帮助。
登录一定要用springsecurity吗
要用。该登录是一定要用该软件的,因为可以安全保护客户信息。SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。
Spring Security系列之核心概念
提到安全,相信大家的第一反应都是加密,什么MD5、AES、SHA-256算法之类东西。没错加密各种算法是安全的基础。针对于基础的安全设施, Java Security 模块 提供了各种安全域的API、PKI、密码学及其内建实现、安全通信、认证、访问控制等丰富的功能,这些都是安全的基础。在Web应用的开发中,对于各种攻击防守,对于认证、授权的实现实现方案是极为重要的。Spring Security项目就是认证、授权、防攻击实现方案的集成框架。学习框架之前,必须要对安全领域的核心的概念进行梳理,这也是 Spring Security 本身所关注的点。
对请求资源的用户身份进行验证的过程,解决的是“这是谁请求的?”的问题。
确认当前用户是否有权限访问资源的过程,解决的是“他能不能做这个操作?”的问题。
跨站请求伪造(Cross Site Request Forgery)
请求是由外部网站伪造发送到目标服务服务器,而不是由用户主动发送请求至目标服务器这种伪造请求就是叫做CSRF。
同步令牌模式(Synchronizer Token Pattern) ,针对Post请求(避免get,会导致令牌泄漏),在请求参数中(一般是头部)增加同步令牌,服务器校验传入令牌的一致性判断是否是正确请求。请求令牌是从Cookie中获取的,由于同源策略外部站点无法获得令牌。能很好的解决该问题。
通常存在用户Session中。为什么不存在cookies中?这是一种早期处理方案,早期一些系统将token存在cookies中但是会出现了漏洞同 Ruby on Rails的CSRF保护绕过一样 ,同时系统失去了紧急情况下对于令牌的失效保护。web应用开发中关键信息存放在后台是一种比较安全的方式。令牌可能会随着Session过期而过期,可以通过主动取或者被动刷新的方式处理。
SameSite 是Http协议中的一个属性, 参考 。这是一种紧急的方案 ,实现防止CSRF攻击,可以在session cookie中设置如下不同的值:
针对cookie中存储了会话信息的请求如果丢失了cookie就会进入授权登录操作。使用SameSite 属性来防攻击要注意一些用户体验的场景,如Strict模式下邮件发送的地址就会失去了登录态,用户就需要再次登录可能会造成不好的体验。
从应用层面讲一般针对于浏览器用户,非浏览器场景就需要禁止CSRF Protection。对CSRF定义中它是依赖浏览器的,容易和中间人攻击混淆。
特别需要注意的具体场景如 login,logout,multipart是要重点进行CSRF保护。
在HTTP协议中有一些首部用于安全处理,本模块对Spring Security中关键安全首部进行讲解。其他参考: Security HTTP Response Headers , developer.mozilla.org
控制浏览器缓存用户浏览内容的首部。Spring Security 默认禁用缓存,可以避免敏感信息的泄露,如用户登录账号查看敏感信息后退出登录恶意用户通过浏览器回退查看到敏感信息。
针对Content-Type不存在的时候是否采用 内容嗅探(content sniffing) 的方式处理控制的首部。为了优化体验浏览器会自动判断响应内容的类型然后进行渲染。禁用sniff可以避免XSS攻击。因为 恶意用户可以创建一个postscript的js文档并用它来执行XSS攻击 。
Http严格传输安全,HTST(Http Strict Transport Security)。很多用户习惯输入不带https协议的域名,中间人可能会拦截到http并窃取https的响应给用户造成中间人攻击。Strict-Transport-Security首部将指导浏览器将其设置为严格的安全传输地址。 参考RFC6797
Http公钥固定(HPKP),用来告诉Web客户端将特定的加密公钥于某个Web服务器相关联,以降低使用证书伪造的MITM攻击的风险。 已被废弃 。
是否允许自己的网站被嵌入到其他网站。用来避免点击劫持(Click Jacking)
针对检测到跨域脚本攻击的中止操作,现代化的浏览器可以通过 Content-Security-Policy 针对不支持CSP的浏览器该保护的是很有效的。该首部没有被全力支持,只有少部分浏览器是支持的如:IE,Safari。
CSP是作为一种向客户端传递安全策略的机制,每一种安全策略代表了一组安全指令,每个指令对资源的呈现做了限制。 Content-Security-Policy , Content-Security-Policy-Report-Only
Referrer 首部标记的是资源最初来源, Referrer-Policy 主要就是控制多少引用信息包含在请求中。
为开发者提供了选择性禁用、启用、修改固定API或者浏览器功能的机制。
提供了通过首部清除浏览器侧数据的方式,例如登出的时候设置首部来清除浏览器侧的相关数据。
【Shiro】一步步的看Shiro 【Shiro与Spring Security区别】
Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相 当简单,Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓 存等
Spring Security是一个提供身份验证、授权和防范常见攻击的框架。它对命令式应用程序和响应式应用程序都提供了一流的支持,是保护基于spring的应用程序的事实上的标准
OAuth for Spring Security为Spring Security提供了一个OAuth实现。支持OAuth提供者和OAuth消费者的实现。支持OAuth 2.0
完美撒花~,下面我会根据Shiro的具体内容结合不同场景的进行讲述以及配置。
有需要讨论Shiro和Spring Security有什么区别或者有什么不同看法的,欢迎留言 大家共同学习
Spring Security PasswordEncoder
Spring Security使用单向密码转换存储密码,也就是加密后的用户密码无法恢复成明文,只能用作密码比较。另外随着计算机性能的提升,传统的SHA-256哈希加密方式不再安全。Spring Security采用了自适应单向加密方式,它通过刻意消耗计算机计算能力来加强密码被破解的难度,比如一个密码加密一次需要100毫秒,可能破解整个系统的密码只需要几小时,如果一个密码加密一次需要1秒那么破解整个系统的密码就需要几天。以 BCryptPasswordEncoder 为例,它内部有一个叫 strength 的工作因素,其值范围是4~31,值越大其循环加密的次数就越多。
当通过 BCryptPasswordEncoder.encode 进行加密的时候, strength 这个参数会被附加到 salt 中, BCrypt.hashpw 通过 salt 获取 strength ,然后通过 BCrypt.crypt_raw 来使用。
在 BCrypt.crypt_raw 中的入参 log_rounds 就是之前提到的 strength ,它通过 rounds = 1 << log_rounds; 左移获得一个循环数,最终通过该循环数提高整个加密过程的计算能力消耗。
PasswordEncoder 接口是Spring Security提供的统一密码接口,主要为整个安全框架提供一个统一的加密过程。其主要的实现类如下:
DelegatingPasswordEncoder 是Spring Security默认使用的加密算法。我们从它的名称其实可以猜测出来它本身并不是一个具体的算法实现类,而是一个算法代理类。这个类主要目的是兼容老旧系统,方便老旧系统的升级改造。
DelegatingPasswordEncoder 可以通 PasswordEncoderFactories.createDelegatingPasswordEncoder() 来创建一个默认的实现方式。
PasswordEncoderFactories.createDelegatingPasswordEncoder() 会首先创建一个 Map ,然后将各种 PasswordEncoder 的具体算法对象存入 Map 中。那么如何使用 DelegatingPasswordEncoder 呢?
以上就是 DelegatingPasswordEncoder 所存储的密码例子,其具体格式如下
其中 {id} 就是所使用的加密算法, encodedPassword 就是 {id} 所对应的具体加密算法加密后的值。
以 {bcrypt} 为例, DelegatingPasswordEncoder 会首先解析出 {bcrypt} ,然后在 Map 中查找具体的实现算法,最终由 BCryptPasswordEncoder 来完成加密或匹配过程。
什么叫spring框架
Spring 框架是一个轻量级的 Java 开发框架,为应用开发提供平台。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一是分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供继承的框架。
Spring的主要特征
1)IOC(控制反转)或DI(依赖注入):明确定义组件的接口,独立开发各个组件,然后根据组件的依赖关系组装运行;即将创建及管理对象的权利交给Spring容器。Spring是一个轻型容器(light-weight Container),其核心是Bean工厂(Bean Factory),用以构造我们所需要的M(Model),能够让相互协作的软件组件保持松散耦合,降低了业务对象替换的复杂性,提高了组件之间的解耦。
2)AOP(面向切面编程):通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。即系统级的服务从代码中解耦出来。例如:将日志记录、性能统计、安全控制、事务处理、异常处理等代码从业务逻辑代码中划分出来,它允许你把遍布应用各处的功能分离出来形成可重用组件。
Sercurity + Auth2框架实现认证授权
SpringSecurity 是Spring家族的一员,是Spring家族提供的安全框架,提供认证和授权功能,最主要的是它提供了简单的使用方式,同时又有很高的灵活性,简单,灵活,强大。
主要的主要的几个配置类
自定义Security的策略
AuthorizationServerConfigurerAdapter
例子如下:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
? ? public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
? ? ? ? endpoints
? ? ? ? ? ? ? ? .authenticationManager(authenticationManager)
? ? ? ? ? ? ? ? .tokenGranter(new CompositeTokenGranter(getTokenGranters(endpoints)))
? ? ? ? ? ? ? ? .userDetailsService(userDetailsService)
? ? ? ? ? ? ? ? .tokenStore(tokenStore());
}
@Override
? ? public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
? ? ? ? //存放到db
? ? ? ? clients.jdbc(dataSource);
//? ? ? ? 存放到内存
? ? ? ? clients.inMemory()
? ? ? ? ? ? ? ? .withClient("webapp")
? ? ? ? ? ? ? ? .secret(pass)
? ? ? ? ? ? ? ? .scopes("server")
? ? ? ? ? ? ? ? .authorizedGrantTypes("password", "authorization_code", "refresh_token")
? ? ? ? ? ? ? ? .accessTokenValiditySeconds(24 * 60 * 60) //token有效期
? ? ? ? ? ? ? ? .refreshTokenValiditySeconds(24 * 60 * 60)
? ? ? ? ? ? ? ? .and().withClient("app")
? ? ? ? ? ? ? ? .secret(pass)
? ? ? ? ? ? ? ? .scopes("app")
? ? ? ? ? ? ? ? .authorizedGrantTypes("authorization_code", "refresh_token")
? ? ? ? ? ? ? ? .redirectUris("https://www.baidu.com/");
? ? }
/**
? ? * 填充认证方式
? ? * @param endpoints endpoints
? ? * @return list
? ? */
? ? private List
getTokenGranters(AuthorizationServerEndpointsConfigurer endpoints) {
? ? ? ? AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices();
? ? ? ? ClientDetailsService clientDetailsService = endpoints.getClientDetailsService();
? ? ? ? OAuth2RequestFactory oAuth2RequestFactory = endpoints.getOAuth2RequestFactory();
? ? ? ? AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();
? ? ? ? return new ArrayList<>(Arrays.asList(
? ? ? ? ? ? ? ? new RefreshTokenGranter(tokenServices, clientDetailsService, oAuth2RequestFactory),
? ? ? ? ? ? ? ? new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService,
? ? ? ? ? ? ? ? ? ? ? ? oAuth2RequestFactory),
? ? ? ? ? ? ? ? new ResourceOwnerPasswordTokenGranter(authenticationManager, endpoints.getTokenServices(),
? ? ? ? ? ? ? ? ? ? ? ? clientDetailsService, oAuth2RequestFactory),
? ? ? ? ? ? ? ? new MyAbstractTokenGranter(authenticationManager, tokenServices, clientDetailsService,
? ? ? ? ? ? ? ? ? ? ? ? oAuth2RequestFactory)));
? ? }
}
默认情况下spring?security?oauth?的http配置。
ResourceServerConfigurerAdapter:
例子如下
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
? ? @Override
? ? public void configure(HttpSecurity http) throws Exception {
? ? ? ? http
? ? ? ? ? ? ? ? .csrf().disable()
? ? ? ? ? ? ? ? .exceptionHandling()
? ? ? ? ? ? ? ? .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
? ? ? ? ? ? ? ? .and()
? ? ? ? ? ? ? ? .authorizeRequests()
? ? ? ? ? ? ? ? .antMatchers("/index/getUser").permitAll()
? ? ? ? ? ? ? ? .antMatchers("/user/getUserPassword").permitAll()
? ? ? ? ? ? ? ? .anyRequest().authenticated()
? ? ? ? ? ? ? ? .and()
? ? ? ? ? ? ? ? .httpBasic();
? ? }
}
WebSecurityConfigurerAdapter 类是个适配器, 需要配置的时候需要我们自己写个配置类去继承,根据自己的需求去进行配置即可WebSecurityConfigurerAdapter的配置拦截要优先于ResourceServerConfigurerAdapter,优先级高的http配置是可以覆盖优先级低的配置的。
WebSecurityConfigurerAdapter
例子如下:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
? ? /**
? ? * 自定义实现用户信息获取
? ? *
? ? * @return
? ? */
? ? @Bean
? ? @Override
? ? public UserDetailsService userDetailsService() {
? ? ? ? return new MyUserDetailsServiceImpl();
? ? }
? ? /**
? ? * @param auth
? ? * @throws Exception
? ? */
? ? @Override
? ? protected void configure(AuthenticationManagerBuilder auth) throws Exception {
? ? ? ? auth.userDetailsService(userDetailsService())
? ? ? ? ? ? ? ? .passwordEncoder(passwordEncoder());
? ? }
? ? /**
? ? * @param http
? ? * @throws Exception
? ? */
? ? @Override
? ? protected void configure(HttpSecurity http) throws Exception {
? ? ? ? http.csrf().disable()
? ? ? ? ? ? ? ? .authorizeRequests().anyRequest().authenticated()
? ? ? ? ? ? ? ? .and()
? ? ? ? ? ? ? ? .httpBasic();
? ? }
? ? /**
? ? * 不定义没有password grant_type
? ? */
? ? @Override
? ? @Bean
? ? public AuthenticationManager authenticationManagerBean() throws Exception {
? ? ? ? return super.authenticationManagerBean();
? ? }
? ? /**
? ? * 密码解析器
? ? * @return PasswordEncoder
? ? */
? ? @Bean
? ? public PasswordEncoder passwordEncoder() {
? ? ? ? return new BCryptPasswordEncoder();
? ? }
}
继承了抽象类 AbstractTokenGranter
AbstractTokenGranter
public class MyAbstractTokenGranter extends AbstractTokenGranter {
? ? @Autowired
? ? private UserInfo userInfo;
? ? @Autowired
? ? private RedisUtil redisUtil;
? ? private static final String GRANT_TYPE = "sms_cod";
? ? private final AuthenticationManager authenticationManager;
? ? public MyAbstractTokenGranter(AuthenticationManager authenticationManager,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AuthorizationServerTokenServices tokenServices,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ClientDetailsService clientDetailsService,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OAuth2RequestFactory requestFactory) {
? ? ? ? this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
? ? }
? ? protected MyAbstractTokenGranter(AuthenticationManager authenticationManager,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AuthorizationServerTokenServices tokenServices,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ClientDetailsService clientDetailsService,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OAuth2RequestFactory requestFactory, String grantType) {
? ? ? ? super(tokenServices, clientDetailsService, requestFactory, grantType);
? ? ? ? this.authenticationManager = authenticationManager;
? ? }
? ? /**
? ? * 我们拿到的 token 终会过期的, 对应于刷新 token模式的 RefreshTokenGranter 则负责获取新的 OAuth2AccessToken。
? ? *
? ? * @param client
? ? * @param tokenRequest
? ? * @return
? ? */
? ? @Override
? ? protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
? ? ? ? // 传入的参数需要有 refresh_token (DefaultOAuth2AccessToken 中有 refreshToken 字段)
? ? ? ? String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
? ? ? ? // 调用 tokenService 的刷新方法得到新的 OAuth2AccessToken
? ? ? ? return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
? ? }
? ? /**
? ? * 用户拦截
? ? *
? ? * @param client? ? ? client
? ? * @param tokenRequest 请求的令牌
? ? * @return
? ? */
? ? @Override
? ? protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
? ? ? ? Map
parameters = new LinkedHashMap
(tokenRequest.getRequestParameters());
? ? ? ? String username = parameters.get("username");
? ? ? ? String password = parameters.get("password");
? ? ? ? String mobile = parameters.get("mobile");
? ? ? ? //客户端提交的验证码
? ? ? ? String smscode = parameters.get("sms_cod");
? ? ? ? String type = parameters.get("type");
? ? ? ? parameters.remove("password");
? ? ? ? //验证用户状态(是否警用等)
//? ? ? ? UserInfo userInfo = new UserInfo();
//? ? ? ? userInfo.setMobile(mobile);
? ? ? ? // 验证验证码
? ? ? ? //获取服务中保存的用户验证码
? ? ? ? String smsCheckCode = (String) redisUtil.get(userInfo.getMobile());
? ? ? ? if (StringUtils.isBlank(smsCheckCode)) {
? ? ? ? ? ? throw new InvalidGrantException("用户没有发送验证码");
? ? ? ? }
? ? ? ? if (!smscode.equals(smsCheckCode)) {
? ? ? ? ? ? throw new InvalidGrantException("验证码不正确");
? ? ? ? } else {
? ? ? ? ? ? //验证通过后从缓存中移除验证码
? ? ? ? ? ? redisUtil.setRemove(userInfo.getMobile(), smsCheckCode);
? ? ? ? }
? ? ? ? if (username.isEmpty()) {
? ? ? ? ? ? throw new InvalidGrantException("用户不存在");
? ? ? ? }
? ? ? ? Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
? ? ? ? ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
? ? ? ? try {
? ? ? ? ? ? userAuth = authenticationManager.authenticate(userAuth);
? ? ? ? } catch (AccountStatusException ase) {
? ? ? ? ? ? //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
? ? ? ? ? ? throw new InvalidGrantException(ase.getMessage());
? ? ? ? } catch (BadCredentialsException e) {
? ? ? ? ? ? // If the username/password are wrong the spec says we should send 400/invalid grant
? ? ? ? ? ? throw new InvalidGrantException(e.getMessage());
? ? ? ? }
? ? ? ? if (userAuth == null || !userAuth.isAuthenticated()) {
? ? ? ? ? ? throw new InvalidGrantException("Could not authenticate user: " + username);
? ? ? ? }
? ? ? ? OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
? ? ? ? return new OAuth2Authentication(storedOAuth2Request, userAuth);
? ? }
核心依赖配置配置如下:
? ? ? ? ? ?
org.springframework.security.oauth
? ? ? ? ? ?
spring-security-oauth2
? ? ? ? ? ?
${oauth2.version}
? ? ? ?
? ? ? ?
? ? ? ? ? ?
org.springframework.cloud
? ? ? ? ? ?
spring-cloud-starter-security
? ? ? ? ? ?
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ?
spring-security-oauth2
? ? ? ? ? ? ? ? ? ?
org.springframework.security.oauth
? ? ? ? ? ? ? ?
? ? ? ? ? ?
使用SpringSrcurity+Anut2根据token实现用户密码登录
service层
/**
? ? * 根据用户名查询
? ? * @param userName 用户名
? ? * @return 返回的路径
? ? */
? ? UserInfo getUserInfoByName(@Param("userName") String userName);
Service层
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {
? ? @Autowired
? ? public PasswordEncoder passwordEncoder;
? ? @Resource
? ? private UserInfoService userInfoService;
? ? @Resource
? ? private UserInfoMapper userInfoMapper;
? ? @Override
? ? public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
? ? ? ? UserInfo userInfo = userInfoService.getUserInfoByName(username);
? ? ? ? if (userInfo == null){
? ? ? ? ? ? throw new UsernameNotFoundException(username);
? ? ? ? }
? ? ? ? return userInfo;
? ? }
Controlleer层
@RestController
@RequestMapping("/")
public class IndexController {
? ? /**
? ? * user
? ? * @param user user
? ? * @return Principal
? ? */
? ? @GetMapping(value = "/user")
? ? public Principal user(Principal user) {
? ? ? ? return user;
? ? }
微服务框架之Spring Cloud简介
在了解 Spring Cloud 之前先了解一下微服务架构需要考量的核心关键点,如下图:
对于以上等核心关键点的处理,不需要我们重复造车轮, Spring Cloud 已经帮我们集成了,它使用 Spring Boot 风格将一些比较成熟的微服务框架组合起来,屏蔽掉了复杂的配置和实现原理,为快速构建微服务架构的应用提供了一套基础设施工具和开发支持。
Spring Cloud 所提供的核心功能包含:
Spring Cloud架构图
Spring Cloud子项目
Spring Cloud 旗下的子项目大致可以分为两类:
如下:
1. Spring Cloud 与 Spring Boot
Spring Boot 可以说是微服务架构的核心技术之一。通过在 Spring Boot 应用中添加 Spring MVC 依赖,就可以快速实现基于 REST 架构的服务接口,并且可以提供对 HTTP 标准动作的支持。而且 Spring Boot 默认提供 JackJson 序列化支持,可以让服务接口输入、输出支持 JSON 等。因此,当使用 Spring Cloud 进行微服务架构开发时,使用 Spring Boot 是一条必经之路。
2. Spring Cloud 与服务治理( Eureka )
服务治理是 Spring Cloud 的核心,在实现上其提供了两个选择,即 Consul 和 Netflix 的 Eureka 。
Eureka 提供了服务注册中心、服务发现客户端,以及注册服务的 UI 界面应用。
在 Eureka 的实现中,节点之间相互平等,有部分注册中心“挂掉”也不会对整个应用造成影响,即使集群只剩一个节点存活,也可以正常地治理服务。即使所有服务注册节点都宕机, Eureka 客户端中所缓存的服务实例列表信息,也可让服务消费者能够正常工作,从而保障微服务之间互相调用的健壮性和应用的弹性。
3. Spring Cloud 与客户端负载均衡( Ribbon )
Ribbon 默认与 Eureak 进行无缝整合,当客户端启动的时候,从 Eureka 服务器中获取一份服务注册列表并维护在本地,当服务消费者需要调用服务时, Ribbon 就会根据负载均衡策略选择一个合适的服务提供者实例并进行访问。
Spring Cloud 通过集成 Netflix 的 Feign 项目,为开发者提供了声明式服务调用,从而简化了微服务之间的调用处理方式。并且默认 Feign 项目集成了 Ribbon ,使得声明式调用也支持客户端负载均衡功能。
4. Spring Cloud 与微服务容错、降级( Hystrix )
为了给微服务架构提供更大的弹性,在 Spring Cloud 中,通过集成 Netflix 下子项目 Hystrix ,通过所提供的 @HystrixCommand 注解可以轻松为我们所开发的微服务提供容错、回退、降级等功能。此外, Hystrix 也默认集成到 Feign 子项目中。
Hystrix 是根据“断路器”模式而创建。当 Hystrix 监控到某服务单元发生故障之后,就会进入服务熔断处理,并向调用方返回一个符合预期的服务降级处理( fallback ),而不是长时间的等待或者抛出调用异常,从而保障服务调用方的线程不会被长时间、不必要地占用,避免故障在应用中的蔓延造成的雪崩效应。
而 Hystrix 的仪表盘项目( Dashboard )可以监控各个服务调用所消耗的时间、请求数、成功率等,通过这种近乎实时的监控和告警,可以及时发现系统中潜在问题并进行处理。
5. Spring Cloud 与服务网关( Zuul )
Spring Cloud 通过集成 Netflix 中的 Zuul 实现 API 服务网关功能,提供对请求的路由和过滤两个功能
路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。
过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。
通过 Zuul ,可以将细粒度的服务组合起来提供一个粗粒度的服务,所有请求都导入一个统一的入口,对外整个服务只需要暴露一个 API 接口,屏蔽了服务端的实现细节。通过 Zuul 的反向代理功能,可以实现路由寻址,将请求转发到后端的粗粒度服务上,并做一些通用的逻辑处理。此外, Zuul 默认会与 Eureka 服务器进行整合,自动从 Eureka 服务器中获取所有注册的服务并进行路由映射,实现 API 服务网关自动配置。
6. Spring Cloud 与消息中间件( Stream )
Spring Cloud 为简化基于消息的开发,提供了 Stream 子项目,通过建立消息应用抽象层,构建了消息收发、分组消费和消息分片等功能处理,将业务应用中的消息收发与具体消息中间件进行解耦,使微服务应用开发中可以非常方便地与 Kafka 和 RabbitMQ 等消息中间件进行集成。
Spring Cloud Bus 基于 Stream 进行扩展,可以作为微服务之间的事件、消息总线,用于服务集群中状态变化的传播。
比如 Spring Cloud Config 借助 Bus ,可以实现配置的动态刷新处理。
7. Spring Cloud 与分布式配置中心( Config )
针对微服务架构下的配置文件管理需求, Spring Cloud 提供了一个 Config 子项目。 Spring Cloud Config 具有中心化、版本控制、支持动态更新和语言独立等特性。
在 Config 子项目中将微服务应用分为两种角色:配置服务器( Config Server )和配置客户端( Config Client )。使用配置服务器集中地管理所有配置属性文件,配置服务中心可以将配置属性文件存储到 Git 、 SVN 等具有版本管理仓库中,也可以存放在文件系统中。默认采用 Git 的方式进行存储,因此可以很容易地对配置文件进行修改,并实现版本控制。
8. Spring Cloud 与微服务链路追踪( Sleuth )
Spring Cloud 中的 Sleuth 子项目为开发者提供了微服务之间调用的链路追踪。
Sleuth 核心思想就是通过一个全局的 ID 将分布在各微服务服务节点上的请求处理串联起来,还原了调用关系,并借助数据埋点,实现对微服务调用链路上的性能数据的采集。
因此,通过 Sleuth 可以很清楚地了解到一个用户请求经过了哪些服务、每个服务处理花费了多长时间,从而可以对用户的请求进行分析。此外,通过将采集的数据发送给 Zipkin 进行存储、统计和分析,从而可以实现可视化的分析和展示,帮助开发者对微服务实施优化处理。
9. Spring Cloud 与微服务安全( Security )
Spring Cloud Security 为我们提供了一个认证和鉴权的安全框架,实现了资源授权、令牌管理等功能,同时结合 Zuul 可以将认证信息在微服务调用过程中直接传递,简化了我们进行安全管控的开发。
Spring Cloud Security 默认支持 OAuth 2.0 认证协议,因此单点登录也可以非常容易实现,并且 OAuth2.0 所生成的令牌可以使用 JWT 的方式,进一步简化了微服务中的安全管理。
10. Spring Cloud 的其他子项目