SpringSecurity源码分析

合集下载

abstractauthenticationtoken源码解析

abstractauthenticationtoken源码解析

`AbstractAuthenticationToken`是Spring Security中的一个抽象类,用于表示一个认证令牌。

它提供了一些基本的认证令牌操作方法,如获取用户名、获取密码、设置密码等。

以下是`AbstractAuthenticationToken`的源码解析:1. 构造函数:```javapublic AbstractAuthenticationToken(Object principal, Object credentials) { this.principal = principal;this.credentials = credentials;}```构造函数接收两个参数,分别是主体(principal)和凭证(credentials)。

这两个参数分别表示认证令牌的主体和凭证信息。

2. getPrincipal()方法:```javaOverridepublic Object getPrincipal() {return this.principal;}```getPrincipal()方法返回认证令牌的主体。

3. getCredentials()方法:```javaOverridepublic Object getCredentials() {return this.credentials;}```getCredentials()方法返回认证令牌的凭证信息。

4. setCredentials()方法:```javapublic void setCredentials(Object credentials) {this.credentials = credentials;}```setCredentials()方法用于设置认证令牌的凭证信息。

5. isAuthenticated()方法:```java@Overridepublic boolean isAuthenticated() {return (this.principal != null);}```isAuthenticated()方法用于判断认证令牌是否已经通过认证。

SpringSecurity实现“记住我”功能及原理解析

SpringSecurity实现“记住我”功能及原理解析

SpringSecurity实现“记住我”功能及原理解析这章继续扩展功能,来⼀个“记住我”的功能实现,就是说⽤户在登录⼀次以后,系统会记住这个⽤户⼀段时间,这段时间内⽤户不需要重新登录就可以使⽤系统。

记住我功能基本原理原理说明⽤户登录发送认证请求的时候会被UsernamePasswordAuthenticationFilter认证拦截,认证成功以后会调⽤⼀个RememberMeService服务,服务⾥⾯有⼀个TokenRepository,这个服务会⽣成⼀个Token,然后将Token写⼊到浏览器的Cookie同时会使⽤TokenRepository把⽣成的Token写到数据库⾥⾯,因为这个动作是在认证成功以后做的,所以在Token写⼊数据库的时候会把⽤户名同时写⼊数据库。

假如浏览器关了重新访问系统,⽤户不需要再次登录就可以访问,这个时候请求在过滤器链上会经过RememberMeAuthenticationFilter,这个过滤器的作⽤是读取Cookie中的Token交给RemeberMeService,RemeberMeService会⽤TokenRepository到数据库⾥去查这个Token在数据库⾥有没有记录,如果有记录就会把⽤户名取出来,取出来以后会进⾏各种校验然后⽣成新Token再调⽤之前的UserDetailService,去获取⽤户的信息,然后把⽤户信息放到SecurityContext⾥⾯,到这⾥就把⽤户给登录上了。

图解说明RememberMeAuthenticationFilter位于过滤器链的哪⼀环?图解⾸先其他认证过滤器会先进⾏认证,当其他过滤器都⽆法认证时,RememberMeAuthenticationFilter会尝试去做认证。

记住我功能具体实现前端页⾯登录的时候加上⼀⾏记住我的勾选按钮,这⾥要注意,name⼀定要是remember-me,下⾯源码部分会提到。

<tr><td colspan='2'><input name="remember-me" type="checkbox" value="true" />记住我</td></tr>后台⾸先配置TokenRepository Bean/*** 记住我功能的Token存取器配置** @return*/@Beanpublic PersistentTokenRepository persistentTokenRepository() {JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 启动的时候⾃动创建表,建表语句 JdbcTokenRepositoryImpl 已经都写好了tokenRepository.setCreateTableOnStartup(true);return tokenRepository;}然后需要在 configure 配置⽅法那边进⾏记住我功能所有组件的配置protected void configure(HttpSecurity http) throws Exception {ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class).formLogin().loginPage("/authentication/require").loginProcessingUrl("/authentication/form").successHandler(meicloudAuthenticationSuccessHandler).failureHandler(meicloudAuthenticationFailureHandler)// 配置记住我功能.and().rememberMe()// 配置TokenRepository.tokenRepository(persistentTokenRepository())// 配置Token过期时间.tokenValiditySeconds(3600)// 最终拿到⽤户名之后,使⽤UserDetailsService去做登录.userDetailsService(userDetailsService).and().authorizeRequests().antMatchers("/authentication/require", securityProperties.getBrowser().getSignInPage(), "/code/image").permitAll().anyRequest().authenticated().and().csrf().disable();}记住我功能Spring Security源码解析登录之前“记住我”源码流程在认证成功之后,会调⽤successfulAuthentication⽅法(这些第五章源码部分已经学习过),在将认证信息保存到Context后,RememberMeServices就会调⽤它的loginSuccess⽅法。

SpringSecurity-UsernamePasswordAuthentication。。。

SpringSecurity-UsernamePasswordAuthentication。。。

SpringSecurity-UsernamePasswordAuthentication。

UsernamePasswordAuthenticationFilter应该是我们最关注的Filter,因为它实现了我们最常⽤的基于⽤户名和密码的认证逻辑。

先看⼀下⼀个常⽤的form-login配置:1<form-login login-page="/login"2 username-parameter="ssoId"3 password-parameter="password"4 authentication-failure-url ="/loginfailure"5 default-target-url="/loginsuccess"/>6<logout invalidate-session="true"/>在这⾥可以⾃定义表单中对应的⽤户名密码的name,已经登录登录成功或失败后跳转的url地址以及登录表单的action。

UsernamePasswordAuthenticationFilter继承虚拟类AbstractAuthenticationProcessingFilter。

AbstractAuthenticationProcessingFilter要求设置⼀个authenticationManager,authenticationManager的实现类将实际处理请求的认证。

AbstractAuthenticationProcessingFilter将拦截符合过滤规则的request,并试图执⾏认证。

⼦类必须实现attemptAuthentication⽅法,这个⽅法执⾏具体的认证。

认证处理:如果认证成功,将会把返回的Authentication对象存放在SecurityContext;然后setAuthenticationSuccessHandler(AuthenticationSuccessHandler)⽅法将会调⽤;这⾥处理认证成功后跳转url的逻辑;可以重新实现AuthenticationSuccessHandler的onAuthenticationSuccess⽅法,实现⾃⼰的逻辑,⽐如需要返回json格式数据时,就可以在这⾥重新相关逻辑。

springsecurity原理

springsecurity原理

springsecurity原理Spring Security是一个基于Java的用于身份验证和授权的安全框架。

它提供了一系列功能和API,帮助开发者实现系统的安全性。

Spring Security的主要原理可以总结为以下几个方面:1. 过滤器链:Spring Security使用过滤器链来拦截所有的HTTP请求并进行安全验证。

每个请求都会经过多个过滤器,在每个过滤器中执行具体的身份验证和授权逻辑。

2. 用户认证:Spring Security提供了多种用户认证的方式,包括基于数据库、LDAP、内存等的认证方式。

开发者可以选择合适的认证方式来验证用户的身份。

认证成功后,认证信息会被保存在SecurityContext中,方便后续的授权操作。

3. 授权策略:Spring Security定义了授权策略来决定系统中不同角色用户的访问权限。

通过配置角色和权限的关系,可以实现对系统资源的精确控制。

开发者可以使用注解或者配置的方式定义授权策略。

4. 安全配置:Spring Security通过一系列的配置来控制系统的安全性。

开发者可以通过配置文件或者编程的方式定义安全规则,包括URL的保护、登录页面的定制、角色和权限的管理等。

5. 常见攻击防护:Spring Security内置了对常见攻击的防护机制,包括跨站点请求伪造(CSRF)、跨站脚本攻击(XSS)、点击劫持等。

开发者可以很方便地使用Spring Security提供的功能来保护应用程序免受这些攻击。

总之,Spring Security通过过滤器链、用户认证、授权策略、安全配置和攻击防护等机制,实现了对系统的身份认证和访问控制,提供了一种简单而灵活的方式来保护应用程序的安全性。

Spring源码分析基本介绍

Spring源码分析基本介绍

Spring源码分析基本介绍摘要:本⽂结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。

若有描述错误之处,欢迎指正。

前⾔作为⼀名开发⼈员,阅读源码是⼀个很好的学习⽅式。

本⽂将结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码,若有描述错误之处,欢迎指正。

Spring是2003年兴起的⼀个轻量级Java开源框架,旨在解决企业应⽤开发的复杂性。

Spring发展⾄今,衍⽣出⾮常丰富的模块,并应⽤在多种场景,⽐如:桌⾯应⽤,Web应⽤等。

Spring的模块化可以允许你只使⽤需要的模块,⽽不必全部引⼊。

⽬录⼀、整体架构1. 核⼼容器2. 数据访问/集成3. Web4. AOP5. Test⼆、设计理念三、使⽤场景1. 典型的Spring web应⽤程序2. Spring中间层使⽤第三⽅web框架3. 远程调⽤4. EJBs-包装现存POJOs⼀、整体架构Spring框架是⼀个分层架构,他包含⼀系列的功能要素,并被分为⼤约20个模块,如下图所⽰(很遗憾,并没有找到Spring5的架构图,下图是Spring4的,但结合Spring5的源码来看,该图还是能够体现Spring5的核⼼模块)这些模块被总结为以下⼏部分。

1. 核⼼容器Core Container(核⼼容器)包含有Core、Beans、Context和Expression Language模块。

Core和Beans模块是框架的基础部分,提供IoC(控制反转)和DI(依赖注⼊)特性。

这⾥的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。

Core模块主要包含Spring框架基本的核⼼⼯具类,Spring的其他组件都要使⽤到这个包⾥的类,Core模块是其他组件的基本核⼼。

当然你也可以在⾃⼰的应⽤系统中使⽤这些⼯具类。

详解springsecurity之httpSecurity使用示例

详解springsecurity之httpSecurity使用示例

详解springsecurity之httpSecurity使⽤⽰例httpSecurity类似于spring security的xml配置⽂件命名空间配置中的<http>元素。

它允许对特定的http请求基于安全考虑进⾏配置。

默认情况下,适⽤于所有的请求,但可以使⽤requestMatcher(RequestMatcher)或者其它相似的⽅法进⾏限制。

使⽤⽰例:最基本的基于表单的配置如下。

该配置将所有的url访问权限设定为⾓⾊名称为"ROLE_USER".同时也定义了内存认证模式:使⽤⽤户名"user"和密码“password”,⾓⾊"ROLE_USER"来认证。

@Configuration@EnableWebSecuritypublic class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/").hasRole("USER").and().formLogin();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");}}配置基于openId的认证⽅式basic⽰例,不使⽤attribute exchange@Configuration@EnableWebSecuritypublic class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) {http.authorizeRequests().antMatchers("/").hasRole("USER").and().openidLogin().permitAll();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication()// the username must match the OpenID of the user you are// logging in with.withUser("https:///accounts/o8/id?id=lmkCn9xzPdsxVwG7pjYMuDgNNdASFmobNkcRPaWU").password("password").roles("USER");}}下⾯展⽰⼀个更⾼级的⽰例,使⽤attribute exchange@Configuration@EnableWebSecuritypublic class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) {http.authorizeRequests().antMatchers("/").hasRole("USER").and().openidLogin().loginPage("/login").permitAll().authenticationUserDetailsService(new AutoProvisioningUserDetailsService()).attributeExchange("https:///.").attribute("email").type("/contact/email").required(true).and().attribute("firstname").type("/namePerson/first").required(true).and().attribute("lastname").type("/namePerson/last").required(true).and().and().attributeExchange(".").attribute("email").type("/contact/email").required(true).and().attribute("fullname").type("/namePerson").required(true).and().and().attributeExchange(".").attribute("email").type("/contact/email").required(true).and().attribute("fullname").type("/namePerson").required(true);}}public class AutoProvisioningUserDetailsService implementsAuthenticationUserDetailsService&lt;OpenIDAuthenticationToken&gt; {public UserDetails loadUserDetails(OpenIDAuthenticationToken token) throws UsernameNotFoundException {return new User(token.getName(), "NOTUSED", AuthorityUtils.createAuthorityList("ROLE_USER"));}}增加响应安全报⽂头默认情况下当使⽤WebSecuirtyConfigAdapter的默认构造函数时激活。

使用Spring Security进行Java应用安全设计

使用Spring Security进行Java应用安全设计

使用Spring Security进行Java应用安全设计Spring Security是一个功能强大且灵活的身份验证和访问控制框架,可以用于Java应用程序的安全设计。

该框架是基于Spring框架的,提供了一套细粒度的安全控制机制,能够帮助开发人员实现用户认证、角色授权以及防止常见的Web应用程序安全漏洞。

一、认证和授权1.用户认证(User Authentication):Spring Security支持多种用户认证方式,如基于数据库的认证、LDAP认证、内存认证等。

开发人员可以选择合适的认证方式,并灵活扩展以满足应用程序的需求。

2.访问控制(Access Control):Spring Security提供了基于角色的访问控制机制,可以根据用户的角色来限制其访问权限。

开发人员可以通过配置文件或编程方式定义角色和拥有该角色的用户的访问权限,从而实现细粒度的访问控制。

二、常见的安全漏洞防护1.跨站脚本攻击(Cross-Site Scripting,XSS):XSS是一种常见的Web应用安全漏洞,可以通过在页面中插入恶意脚本来攻击用户。

Spring Security可以自动对用户输入进行编码,从而防止XSS攻击。

2.跨站请求伪造(Cross-Site Request Forgery,CSRF):CSRF是一种攻击方式,通过伪造用户的请求来执行恶意操作。

Spring Security可以为每个请求生成唯一的标识符,并要求用户在提交请求时携带该标识符,从而有效防止CSRF攻击。

3.注入攻击(Injection):注入攻击是一种利用应用程序没有正确校验和过滤用户输入的漏洞。

Spring Security提供了多种防止注入攻击的策略,如使用预编译查询、使用ORM框架等。

4.密码安全:Spring Security提供了灵活且安全的密码存储和校验机制。

可以使用强哈希算法、加盐等方式保护用户的密码安全。

5.审计和日志:Spring Security可以记录用户的登录、注销、访问记录等操作,并提供丰富的审计和日志功能。

SpringBoot整合升级SpringSecurity报错【Therequestwasr。。。

SpringBoot整合升级SpringSecurity报错【Therequestwasr。。。

SpringBoot整合升级SpringSecurity报错【Therequestwasr。

前⾔最近LZ给项⽬框架升级,从Spring1.x升级到Spring2.x, 在这⾥就不多赘述两个版本之间的区别以及升级的原因。

关于升级过程中踩的坑,在其他博⽂中会做⽐较详细的记录,以便给读者参考,不要掉进同样的坑⾥。

这⾥我们讨论⼀个关于URL中包含双斜杠被拦截的问题。

发现问题升级框架之后,测试⼀个功能时,发现报错Http 500,第⼀时间怀疑是后台功能报错。

打印后台错误⽇志,发现报错信息:The request was rejected because the URL was not normalized。

之后与升级前相同环境对⽐发现,相同的功能,升级之后,URL中包含双斜杠。

分析问题经过对⽐不同和错误信息,初步定位问题出在URL上。

查询资料得知,Spring Security 在⾼版本中增加了StrictHttpFirewall类,对URL校验更加严格。

于是查看源码:private static boolean isNormalized(String path) {if (path == null) {return true;} else if (path.indexOf("//") > -1) {return false;} else {int i;for(int j = path.length(); j > 0; j = i) {i = stIndexOf(47, j - 1);int gap = j - i;if (gap == 2 && path.charAt(i + 1) == '.') {return false;}if (gap == 3 && path.charAt(i + 1) == '.' && path.charAt(i + 2) == '.') {return false;}}return true;}}解决问题⽅法⼀:修改项⽬中出现“//”双斜杠的URL路径,哈哈⽅法⼆:⾃定义FireWall⽅式允许URL出现双斜杠“//”参考:Spring 5.0.3 RequestRejectedException: The request was rejected because the URL was not normalized1. 创建允许在URL中使⽤斜线的⾃定义防⽕墙。

Spring_Security API解读

Spring_Security API解读

用户相关的信息是通过erDetailsService接口来加载的。

该接口的唯一方法是loadUserByUsername(String username),用来根据用户名加载相关的信息。

这个方法的返回值是erDetails接口类型,其中包含了用户的信息,包括用户名、密码、权限。

是否启用、是否锁定、是否过期等。

其中最重要的是用户权限,由org.springframework.security.core.GrantedAuthority接口来表示。

虽然Spring Security内部的设计和实现比较复杂,但是一般情况下,开发人员只需要使用它默认提供的实现就可以满足绝大多数情况的需求,而且只需要简单的配置声明即可。

使用数据库加载用户信息的时候,所生成的实体类必须有一些基本的信息,这个是框架的约定,这个约定是通过定义UserDetails接口来实现的,也就是你要用我这个框架,那么你就必须给我提供这些信息。

public interface UserDetailsextends SerializableProvides core user information.Implementations are not used directly by Spring Security for security purposes. They simply store user information which is later encapsulated into Authentication objects. This allows non-security related user information (such as email addresses, telephone numbers etc) to be stored in a convenient location.Concrete implementations must take particular care to ensure the non-null contract detailed for each method is enforced. See User for a reference implementation (which you might like to extend).Concrete implementations should be immutable (value object semantics, like a String). This is because the UserDetails will be stored in caches and as such multiple threads may use the same instance.public interface UserDetailsServiceDefines an interface for implementations that wish to provide data access services to the DaoAuthenticationProvider.The interface requires only one read-only method, which simplifies support of new data access strategies.1、声明使用数据库来保存用户信息首先定义了一个使用Apache Derby数据库的数据源,Spring Security的erdetails.jdbc.JdbcDaoImpl类使用该数据源来加载用户信息。

springmvc工作原理和源码分析

springmvc工作原理和源码分析

springmvc⼯作原理和源码分析springmvc⼯作原理以及源码分析(基于spring3.1.0) springmvc是⼀个基于spring的web框架.本篇⽂章对它的⼯作原理以及源码进⾏深⼊分析. ⼀、springmvc请求处理流程 ⼆、springmvc的⼯作机制 三、springmvc核⼼源码分析 四、谈谈springmvc的优化⼀、springmvc请求处理流程 引⽤spring in action上的⼀张图来说明了springmvc的核⼼组件和请求处理流程:①:DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件. ②:HanlerMapping是springmvc中完成url到controller映射的组件.DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller. ③:Cntroller处理request,并返回ModelAndView对象,Controller是springmvc中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件. ④⑤⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端.⼆、springmvc的⼯作机制 在容器初始化时会建⽴所有url和controller的对应关系,保存到Map<url,controller>中.tomcat启动时会通知spring初始化容器(加载bean的定义信息和初始化所有单例bean),然后springmvc会遍历容器中的bean,获取每⼀个controller中的所有⽅法访问的url,然后将url和controller保存到⼀个Map中; 这样就可以根据request快速定位到controller,因为最终处理request的是controller中的⽅法,Map中只保留了url和controller中的对应关系,所以要根据request的url进⼀步确认controller中的method,这⼀步⼯作的原理就是拼接controller的url(controller上@RequestMapping的值)和⽅法的url(method上@RequestMapping的值),与request的url进⾏匹配,找到匹配的那个⽅法; 确定处理请求的method后,接下来的任务就是参数绑定,把request中参数绑定到⽅法的形式参数上,这⼀步是整个请求处理过程中最复杂的⼀个步骤。

盘点SpringSecurity框架中的八大经典设计模式【转】

盘点SpringSecurity框架中的八大经典设计模式【转】

盘点SpringSecurity框架中的⼋⼤经典设计模式【转】上次有⼩伙伴建议,源码分析太枯燥了,要是能够结合设计模式⼀起来,这样更有助于⼤家理解 Spring Security 源码,同时还能复习⼀波设计模式。

因此松哥今天就试着整⼀篇,和⼤家来聊⼀聊 Spring Security 中涉及到的设计模式,不过 Spring Security 中涉及到的设计模式还是⾮常多的,松哥这⾥讲⼏个,剩下的欢迎⼩伙伴们留⾔补充。

1.模板⽅法模式❝Template Pattern(模板⽅法模式)是⼀个抽象类公开定义了执⾏它的⽅法的模板。

它的⼦类可以按需要重写⽅法实现,但调⽤将以抽象类中定义的⽅式进⾏,这是⼀种⾏为型模式。

模板⽅法⽅式优点如下:在⽗类中提取了公共的部分代码,便于代码复⽤和扩展。

部分⽅法是由⼦类实现的,⼦类可以通过扩展⽅式增加相应的功能,符合开闭原则。

缺点如下:对每个不同的实现都需要定义⼀个⼦类,导致类的个数增加,系统更加复杂,设计也更加抽象。

⽗类中的抽象⽅法由⼦类实现,⼦类执⾏的结果会影响⽗类的结果,增加了代码理解难度。

介绍完模板⽅法模式,⼤家可能⼤概猜到了 Spring Security 中哪些地⽅⽤到模板⽅法模式了。

我举⼏个简单的例⼦。

第⼀个例⼦是 AbstractUserDetailsAuthenticationProvider 类的设计。

⼤家都知道这个类是⽤来做验证的,认证的逻辑在这个⽅法中都定义好了,但是该类却定义了两个抽象⽅法:retrieveUser 该⽅法⽤户从数据源中获取⽤户对象。

additionalAuthenticationChecks 该⽅法⽤来做额外的校验(登录凭证的校验)这两个抽象⽅法是在 DaoAuthenticationProvider 中实现的。

DaoAuthenticationProvider 的实现就是从数据库中加载⽤户,默认检验登录凭证也都是验证密码。

如果你的数据源来⾃其他地⽅,或者登录凭证不是密码,那么⾃定义类继承⾃ AbstractUserDetailsAuthenticationProvider 并重写它⾥边的这两个⽅法即可。

SpringSecurity解析(六)——基于JWT的单点登陆(SSO)开发及原理解析

SpringSecurity解析(六)——基于JWT的单点登陆(SSO)开发及原理解析

SpringSecurity解析(六)——基于JWT的单点登陆(SSO)开发及原理解析Spring Security 解析(六) —— 基于JWT的单点登陆(SSO)开发及原理解析 在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是⼀知半解,因此决定先把Spring Security 、Spring Security Oauth2 等权限、认证相关的内容、原理及设计学习并整理⼀遍。

本系列⽂章就是在学习的过程中加强印象和理解所撰写的,如有侵权请告知。

项⽬环境:JDK1.8Spring boot 2.xSpring Security 5.x 单点登录(Single Sign On),简称为SSO,是⽬前⽐较流⾏的企业业务整合的解决⽅案之⼀。

SSO的定义是在多个应⽤系统中,⽤户只需要登录⼀次就可以访问所有相互信任的应⽤系统。

单点登陆本质上也是OAuth2的使⽤,所以其开发依赖于授权认证服务,如果不清楚的可以看我的上⼀篇⽂章。

⼀、单点登陆 Demo开发 从单点登陆的定义上来看就知道我们需要新建个应⽤程序,我把它命名为 security-sso-client。

接下的开发就在这个应⽤程序上了。

⼀、Maven 依赖 主要依赖 spring-boot-starter-security、spring-security-oauth2-autoconfigure、spring-security-oauth2 这3个。

其中 spring-security-oauth2-autoconfigure 是Spring Boot 2.X 才有的。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--@EnableOAuth2Sso 引⼊,Spring Boot 2.x 将这个注解移到该依赖包--><dependency><groupId>org.springframework.security.oauth.boot</groupId><artifactId>spring-security-oauth2-autoconfigure</artifactId><exclusions><exclusion><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId></exclusion></exclusions><version>2.1.7.RELEASE</version></dependency><!-- 不是starter,⼿动配置 --><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><!--请注意下 spring-authorization-oauth2 的版本务必⾼于 2.3.2.RELEASE,这是官⽅的⼀个bug:ng.NoSuchMethodError: org.springframework.data.redis.connection.RedisConnection.set([B[B)V要求必须⼤于2.3.5 版本,官⽅解释:https:///BUG9/spring-security/network/alert/pom.xml/org.springframework.security.oauth:spring-security-oauth2/open--><version>2.3.5.RELEASE</version></dependency>⼆、单点配置 @EnableOAuth2Sso 单点的基础配置引⼊是依赖 @EnableOAuth2Sso 实现的,在Spring Boot 2.x 及以上版本的 @EnableOAuth2Sso 是在 spring-security-oauth2-autoconfigure 依赖⾥的。

Spring Security学习总结二

Spring Security学习总结二

Spring Security学习总结二前一篇文章里介绍了Spring Security的一些基础知识,相信你对Spring Security的工作流程已经有了一定的了解,如果你同时在读源代码,那你应该可以认识的更深刻。

在这篇文章里,我们将对Spring Security进行一些自定义的扩展,比如自定义实现UserDetailsService,保护业务方法以及如何对用户权限等信息进行动态的配置管理。

一自定义UserDetailsService实现UserDetailsService接口,这个接口中只定义了唯一的UserDetails loadUserByUsername(String username)方法,它通过用户名来获取整个UserDetails对象。

前一篇文章已经介绍了系统提供的默认实现方式InMemoryDaoImpl,它从配置文件中读取用户的身份信息(用户名,密码等),如果你的客户想修改用户信息,就需要直接修改配置文件(你需要告诉用户配置文件的路径,应该在什么地方修改,如何把明文密码通过MD5加密以及如何重启服务器等)。

听起来是不是很费劲啊!在实际应用中,我们可能需要提供动态的方式来获取用户身份信息,最常用的莫过于数据库了,当然也可以是LDAP服务器等。

本文首先介绍系统提供的一个默认实现类 JdbcDaoImpl(erdetails.jdbc. JdbcDaoImpl),它通过用户名从数据库中获取用户身份信息,修改配置文件,将userDetailsService Bean的配置修改如下:1<bean id="userDetailsService"2class="erdetails.jdbc.JdbcDaoImpl"3p:dataSource-ref="dataSource"4p:usersByUsernameQuery="select userName, passWord, enabled, from users where userName=?"5p:authoritiesByUsernameQuery="selecterName,r.roleName from users u,roles7r,users_roles ur where erId=erId and8r.roleId=ur.roleId and erName=?"/>JdbcDaoImpl类继承自Spring Framework的JdbcDaoSupport类并实现了UserDetailsService接口,因为从数据库中读取信息,所以首先需要一个数据源对象,这里不在多说,这里需要重点介绍的是usersByUsernameQuery和authoritiesByUsernameQuery,属性,它们的值都是一条SQL语句,JdbcDaoImpl 类通过SQL从数据库中检索相应的信息,usersByUsernameQuery属性定义了通过用户名检索用户信息的SQL语句,包括用户名,密码以及用户是否可用,authoritiesByUsernameQuery属性定义了通过用户名检索用户权限信息的SQL 语句,这两个属性都引用一个MappingSqlQuery(请参考Spring Framework相关资料)实例,MappingSqlQuery的mapRow()方法将一个ResultSet(结果集)中的字段映射为一个领域对象,Spring Security为我们提供了默认的数据库表,如下图所示(摘自《Spring in Action》):图<!——[if supportFields]——>1<!——[if supportFields]——> JdbcDaoImp数据库表如果我们需要获取用户的其它信息就需要自己来扩展系统的默认实现,首先应该了解一下UserDetailsService实现的原理,还是要回到源代码,以下是JdbcDaoImpl类的部分代码:1private class UsersByUsernameMapping extends MappingSqlQuery {3protected UsersByUsernameMapping(DataSource ds) {5super(ds, usersByUsernameQuery);7declareParameter(new SqlParameter(Types.VARCHAR));9compile();11}13protected Object mapRow(ResultSet rs, int rownum) throws SQLException {15String username = rs.getString(1);17String password = rs.getString(2);19boolean enabled = rs.getBoolean(3);21UserDetails user = new User(username, password, enabled, true,23true, true, new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")}); 25return user;26}28}也许你已经看出什么来了,对了,系统返回的UserDetails对象就是从这里来的,这就是读源代码的好处,DaoAuthenticationProvider提供者通过调用自己的authenticate(Authentication authentication)方法将用户在登录页面输入的用户信息与这里从数据库获取的用户信息进行匹配,如果匹配成功则将用户的权限信息赋给 Authentication对象并将其存放在SecurityContext 中,供其它请求使用。

SpringsecurityBCryptPasswordEncoder密码验证原理详解

SpringsecurityBCryptPasswordEncoder密码验证原理详解

SpringsecurityBCryptPasswordEncoder密码验证原理详解⼀、加密算法和hash算法的区别加密算法是⼀种可逆的算法,基本过程就是对原来为明⽂的⽂件或数据按某种算法进⾏处理,使其成为不可读的⼀段代码为“密⽂”,但在⽤相应的密钥进⾏操作之后就可以得到原来的内容。

哈希算法是⼀种不可逆的算法,是把任意长度的输⼊通过散列算法变换成固定长度的输出,输出就是散列值,不同的输⼊可能会散列成相同的输出,所以不可能从散列值来确定唯⼀的输⼊值。

⼆、源码解析BCryptPasswordEncoder类实现了PasswordEncoder接⼝,这个接⼝中定义了两个⽅法public interface PasswordEncoder {String encode(CharSequence rawPassword);boolean matches(CharSequence rawPassword, String encodedPassword);}其中encode(...)是对字符串进⾏加密的⽅法,matches使⽤来校验传⼊的明⽂密码rawPassword是否和加密密码encodedPassword相匹配的⽅法。

即对密码进⾏加密时调⽤encode,登录认证时调⽤matches下⾯我们来看下BCryptPasswordEncoder类中这两个⽅法的具体实现1. encode⽅法public String encode(CharSequence rawPassword) {String salt;if (strength > 0) {if (random != null) {salt = BCrypt.gensalt(strength, random);}else {salt = BCrypt.gensalt(strength);}}else {salt = BCrypt.gensalt();}return BCrypt.hashpw(rawPassword.toString(), salt);}可以看到,这个⽅法中先基于某种规则得到了⼀个盐值,然后在调⽤BCrypt.hashpw⽅法,传⼊明⽂密码和盐值salt。

SpringSecurity3源码分析-BasicAuthenticationFilter

SpringSecurity3源码分析-BasicAuthenticationFilter

SpringSecurity3源码分析-BasicAuthenticationFilter BasicAuthenticationFilter过滤器对应的类路径为org.springframework.security.web.authentication.www.BasicAuthenticationFilterBasic验证⽅式相⽐较⽽⾔⽤的不是太多。

spring security也⽀持basic的⽅式,配置如下Xml代码1. <security:http auto-config="true">2. <!-- <security:form-login login-page="/login.jsp"/>-->3. <security:http-basic/>4. <security:logout logout-success-url="/login.jsp" invalidate-session="true"/>5. <security:intercept-url pattern="/login.jsp*" filters="none"/>6. <security:intercept-url pattern="/admin.jsp*" access="ROLE_ADMIN"/>7. <security:intercept-url pattern="/index.jsp*" access="ROLE_USER,ROLE_ADMIN"/>8. <security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>9. </security:http>如果选择basic⽅式,需要把form-login标签的定义给注释掉。

SpringBoot集成SpringSecurity用JWT令牌实现登录和鉴权的方法

SpringBoot集成SpringSecurity用JWT令牌实现登录和鉴权的方法

SpringBoot集成SpringSecurity⽤JWT令牌实现登录和鉴权的⽅法最近在做项⽬的过程中需要⽤JWT做登录和鉴权查了很多资料都不甚详细有的是需要在application.yml⾥进⾏jwt的配置但我在导包后并没有相应的配置项因⽽并不适⽤在踩过很多坑之后稍微整理了⼀下做个笔记⼀、概念1、什么是JWTJson Web Token (JWT)是为了在⽹络应⽤环境间传递声明⽽执⾏的⼀种基于JSON的开放标准(RFC 7519)该token被设计为紧凑且安全的特别适⽤于分布式站点的单点登录(SSO)场景随着JWT的出现使得校验⽅式更加简单便捷化JWT实际上就是⼀个字符串它由三部分组成:头部载荷和签名⽤[.]分隔这三个部分最终的格式类似于:xxxx.xxxx.xxxx在服务器直接根据token取出保存的⽤户信息即可对token的可⽤性进⾏校验使得单点登录更为简单2、JWT校验的过程1、浏览器发送⽤户名和密码发起登录请求2、服务端验证⾝份根据算法将⽤户标识符打包⽣成token字符串并且返回给浏览器3、当浏览器需要发起请求时将token⼀起发送给服务器4、服务器发现数据中携带有token 随即进⾏解密和鉴权5、校验成功服务器返回请求的数据⼆、使⽤1、⾸先是导包<!-- Spring Security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- Spring Security和JWT整合 --><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.0.10.RELEASE</version></dependency><!-- JWT --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><!-- 字符串转换需要⽤到此包 --><dependency><groupId>mons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency>2、实体类两个实体类⼀个是⽤户另⼀个是权限public class User {private Integer id;private String username;private String password;省略gettersetter之类的代码...}public class Role {private Integer id;private String username;private String name;省略gettersetter之类的代码...}3、然后需要⼀个Utils⼯具类该类⽤于进⾏Token的加密和解密可在此类中单元测试import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import java.util.Date;import java.util.HashMap;import java.util.Map;public class JwtTokenUtil {// Token请求头public static final String TOKEN_HEADER = "Authorization";// Token前缀public static final String TOKEN_PREFIX = "Bearer ";// 签名主题public static final String SUBJECT = "piconjo";// 过期时间public static final long EXPIRITION = 1000 * 24 * 60 * 60 * 7;// 应⽤密钥public static final String APPSECRET_KEY = "piconjo_secret";// ⾓⾊权限声明private static final String ROLE_CLAIMS = "role";/*** ⽣成Token*/public static String createToken(String username,String role) {Map<String,Object> map = new HashMap<>();map.put(ROLE_CLAIMS, role);String token = Jwts.builder().setSubject(username).setClaims(map).claim("username",username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRITION)).signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();return token;}/*** 校验Token*/public static Claims checkJWT(String token) {try {final Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims;} catch (Exception e) {e.printStackTrace();return null;}}/*** 从Token中获取username*/public static String getUsername(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("username").toString();}/*** 从Token中获取⽤户⾓⾊*/public static String getUserRole(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("role").toString();}/*** 校验Token是否过期*/public static boolean isExpiration(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.getExpiration().before(new Date());}}4、配置UserDetailsService的实现类⽤于加载⽤户信息import xxx.xxx.xxx.bean.Role; // ⾃⼰的包import er; // ⾃⼰的包import erMapper; // ⾃⼰的包import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.authority.SimpleGrantedAuthority;import erDetails;import erDetailsService;import ernameNotFoundException;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {if (s == null || "".equals(s)){throw new RuntimeException("⽤户不能为空");}// 调⽤⽅法查询⽤户User user = userMapper.findUserByUsername(s);if (user == null){throw new RuntimeException("⽤户不存在");}List<SimpleGrantedAuthority> authorities = new ArrayList<>();for (Role role:userMapper.findRoleByUsername(s)){authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));}return new er(user.getUsername(),"{noop}"+user.getPassword(),authorities); }}5、然后配置两个拦截器其中⼀个⽤于登录另⼀个⽤于鉴权JWTAuthenticationFilter登录拦截器:该拦截器⽤于获取⽤户登录的信息⾄于具体的验证只需创建⼀个token并调⽤authenticationManager的authenticate()⽅法让Spring security验证即可验证的事交给框架import com.alibaba.fastjson.JSON;import xxx.xxx.xxx.utils.JwtTokenUtil; // ⾃⼰的包import org.springframework.security.authentication.*;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.core.GrantedAuthority;import er;import ernamePasswordAuthenticationFilter;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.Collection;/*** 验证⽤户名密码正确后⽣成⼀个token并将token返回给客户端*/public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {private AuthenticationManager authenticationManager;public JWTAuthenticationFilter(AuthenticationManager authenticationManager){this.authenticationManager = authenticationManager;}/*** 验证操作接收并解析⽤户凭证*/@Overridepublic Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {// 从输⼊流中获取到登录的信息// 创建⼀个token并调⽤authenticationManager.authenticate() 让Spring security进⾏验证return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getParameter("username"),request.getParameter("password")));}/*** 验证【成功】后调⽤的⽅法* 若验证成功⽣成token并返回*/@Overrideprotected void successfulAuthentication(HttpServletRequest request,HttpServletResponse response,FilterChain chain,Authentication authResult) throws IOException {User user= (User) authResult.getPrincipal();// 从User中获取权限信息Collection<? extends GrantedAuthority> authorities = user.getAuthorities();// 创建TokenString token = JwtTokenUtil.createToken(user.getUsername(), authorities.toString());// 设置编码防⽌乱码问题response.setCharacterEncoding("UTF-8");response.setContentType("application/json; charset=utf-8");// 在请求头⾥返回创建成功的token// 设置请求头为带有"Bearer "前缀的token字符串response.setHeader("token", JwtTokenUtil.TOKEN_PREFIX + token);// 处理编码⽅式防⽌中⽂乱码response.setContentType("text/json;charset=utf-8");// 将反馈塞到HttpServletResponse中返回给前台response.getWriter().write(JSON.toJSONString("登录成功"));}/*** 验证【失败】调⽤的⽅法*/@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { String returnData="";// 账号过期if (failed instanceof AccountExpiredException) {returnData="账号过期";}// 密码错误else if (failed instanceof BadCredentialsException) {returnData="密码错误";}// 密码过期else if (failed instanceof CredentialsExpiredException) {returnData="密码过期";}// 账号不可⽤else if (failed instanceof DisabledException) {returnData="账号不可⽤";}//账号锁定else if (failed instanceof LockedException) {returnData="账号锁定";}// ⽤户不存在else if (failed instanceof InternalAuthenticationServiceException) {returnData="⽤户不存在";}// 其他错误else{returnData="未知异常";}// 处理编码⽅式防⽌中⽂乱码response.setContentType("text/json;charset=utf-8");// 将反馈塞到HttpServletResponse中返回给前台response.getWriter().write(JSON.toJSONString(returnData));}}JWTAuthorizationFilter权限校验拦截器:当访问需要权限校验的URL(当然该URL也是需要经过配置的) 则会来到此拦截器在该拦截器中对传来的Token进⾏校验只需告诉Spring security该⽤户是否已登录并且是什么⾓⾊拥有什么权限即可import xxx.xxx.xxx.utils.JwtTokenUtil; // ⾃⼰的包import ng3.StringUtils;import org.springframework.security.authentication.AuthenticationManager;import ernamePasswordAuthenticationToken;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;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;/*** 登录成功后⾛此类进⾏鉴权操作*/public class JWTAuthorizationFilter extends BasicAuthenticationFilter {public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {super(authenticationManager);}/*** 在过滤之前和之后执⾏的事件*/@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws IOException, ServletException {String tokenHeader = request.getHeader(JwtTokenUtil.TOKEN_HEADER);// 若请求头中没有Authorization信息或是Authorization不以Bearer开头则直接放⾏if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)){chain.doFilter(request, response);return;}// 若请求头中有token 则调⽤下⾯的⽅法进⾏解析并设置认证信息SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));super.doFilterInternal(request, response, chain);}/*** 从token中获取⽤户信息并新建⼀个token** @param tokenHeader 字符串形式的Token请求头* @return 带⽤户名和密码以及权限的Authentication*/private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) {// 去掉前缀获取Token字符串String token = tokenHeader.replace(JwtTokenUtil.TOKEN_PREFIX, "");// 从Token中解密获取⽤户名String username = JwtTokenUtil.getUsername(token);// 从Token中解密获取⽤户⾓⾊String role = JwtTokenUtil.getUserRole(token);// 将[ROLE_XXX,ROLE_YYY]格式的⾓⾊字符串转换为数组String[] roles = StringUtils.strip(role, "[]").split(", ");Collection<SimpleGrantedAuthority> authorities=new ArrayList<>();for (String s:roles){authorities.add(new SimpleGrantedAuthority(s));}if (username != null){return new UsernamePasswordAuthenticationToken(username, null,authorities);}return null;}}6、再配置⼀个⾃定义类⽤于进⾏匿名⽤户访问资源时⽆权限的处理该类需实现AuthenticationEntryPointimport com.alibaba.fastjson.JSONObject;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;public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {response.setCharacterEncoding("utf-8");response.setContentType("text/javascript;charset=utf-8");response.getWriter().print(JSONObject.toJSONString("您未登录,没有访问权限"));}}7、最后将这些组件组装到⼀起即可创建⼀个⾃定义的配置类继承WebSecurityConfigurerAdapter在该类上需加@EnableWebSecurity注解配置Web安全过滤器和启⽤全局认证机制import xxx.xxx.xxx.JWTAuthenticationEntryPoint; // ⾃⼰的包import xxx.xxx.xxx.xxx.JWTAuthenticationFilter; // ⾃⼰的包import xxx.xxx.xxx.xxx.JWTAuthorizationFilter; // ⾃⼰的包import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.context.annotation.Bean;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity;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 erDetailsService;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.CorsConfigurationSource;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowired@Qualifier("userDetailsServiceImpl")private UserDetailsService userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {erDetailsService(userDetailsService);}/*** 安全配置*/@Overrideprotected void configure(HttpSecurity http) throws Exception {// 跨域共享http.cors().and()// 跨域伪造请求限制⽆效.csrf().disable().authorizeRequests()// 访问/data需要ADMIN⾓⾊.antMatchers("/data").hasRole("ADMIN")// 其余资源任何⼈都可访问.anyRequest().permitAll().and()// 添加JWT登录拦截器.addFilter(new JWTAuthenticationFilter(authenticationManager()))// 添加JWT鉴权拦截器.addFilter(new JWTAuthorizationFilter(authenticationManager())).sessionManagement()// 设置Session的创建策略为:Spring Security永不创建HttpSession 不使⽤HttpSession来获取SecurityContext .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 异常处理.exceptionHandling()// 匿名⽤户访问⽆权限资源时的异常.authenticationEntryPoint(new JWTAuthenticationEntryPoint());}/*** 跨域配置* @return 基于URL的跨域配置信息*/@BeanCorsConfigurationSource corsConfigurationSource() {final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();// 注册跨域配置source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());return source;}}定义⼀个⽤于测试的对外映射接⼝:@RestControllerpublic class UserController {@GetMapping("/data")private ResponseUtil data(){return "This is data.";}}默认登录路径是/login⽤POST请求发送若要修改默认的登录路径只需要在⾃⼰定义的登录过滤器JWTAuthenticationFilter的构造⽅法⾥进⾏配置即可⽐如若想修改为/api/login:public JWTAuthenticationFilter(AuthenticationManager authenticationManager){this.authenticationManager = authenticationManager;// 设置登录URLsuper.setFilterProcessesUrl("/api/login");}登录时参数的属性名分别是username和password 不能改动:登录成功后会返回⼀个Token:在请求需要权限的接⼝路径时若不带上Token 则会提⽰没有访问权限带上Token后再次请求即可正常访问:注:Token的前⾯要带有Bearer的前缀这样⼀个基本的实现就差不多完成了为简单演⽰在该案例中就不对密码进⾏加密了实际开发是需要对明⽂密码加密后存储的推荐⽤BCrypt进⾏加密和解密为节省篇幅⽤于注册的接⼝也不写了实际上在注册接⼝传⼊的密码也需要⽤BCrypt加密后再存⼊数据库中还可以⽤Redis进⾏Token的存储这些都是后话了到此这篇关于SpringBoot集成Spring Security⽤JWT令牌实现登录和鉴权的⽅法的⽂章就介绍到这了,更多相关SpringBoot JWT令牌登录和鉴权内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。

preauthorize注解源码

preauthorize注解源码

preauthorize注解源码1. preauthorize注解概述preauthorize注解是Spring框架中用于对方法进行授权访问控制的注解。

它基于Spring Security框架,允许我们对方法进行精细的授权控制,限制只有特定角色或权限的用户才能访问该方法。

本文将对preauthorize注解的源码进行深入分析,帮助读者更好地理解和使用该注解。

2. preauthorize注解的定义在Spring Security框架中,preauthorize注解的定义如下:```javaTarget({ElementType.METHOD, ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME)DocumentedInheritedSecuredpublic interface PreAuthorize {String value();}```从上述定义可以看出,preauthorize注解是一个标注了Secured注解的自定义注解,它可以应用在方法和类型上,用来指定访问控制的表达式。

3. preauthorize注解的使用示例下面是一个简单的preauthorize注解的使用示例:```javaControllerpublic class MyController {PreAuthorize("hasRole('ROLE_ADMIN')")RequestMapping("/adminPage")public String adminPage() {// do somethingreturn "adminPage";}}```在上面的示例中,adminPage方法上使用了PreAuthorize注解,并指定了访问控制的表达式"hasRole('ROLE_ADMIN')",表示只有拥有ROLE_ADMIN角色的用户才能访问adminPage方法。

springsecurity源码学习(五)AuthenticationManager、Pr。。。

springsecurity源码学习(五)AuthenticationManager、Pr。。。

springsecurity源码学习(五)AuthenticationManager、Pr。

在上上篇中对AuthenticationManager做过简单的介绍,这⾥,我们详细分析下AuthenticationManager。

先简单描述下标题的这3个类有啥关系,AuthenticationManager是Spring Security⽤来认证的,ProviderManager是AuthenticationManager 的⼀个实现,⽽AuthenticationConfiguration则是诞⽣(初始化)AuthenticationManager的配置类。

AuthenticationManager的初始化是在HttpSecurity初始化时,顺带初始化的。

开玩笑,AuthenticationManager也是⾮常重要的。

让我们再回顾下protected final HttpSecurity getHttp() throws Exception {if (http != null) {return http;}DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);AuthenticationManager authenticationManager = authenticationManager();authenticationBuilder.parentAuthenticationManager(authenticationManager);Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();。

SpringSecurity--FilterInvocationSecurityMetad。。。

SpringSecurity--FilterInvocationSecurityMetad。。。

SpringSecurity--FilterInvocationSecurityMetad。

FilterInvocationSecurityMetadataSource前⾔FilterInvocationSecurityMetadataSource 翻译过来叫做:过滤器调⽤安全元数据源。

⼀般情况下,我们如果需要⾃定义权限拦截,则需要涉及到 FilterInvocationSecurityMetadataSource 这个接⼝了。

继承关系FilterInvocationSecurityMetadataSource 有⼀个默认的实现类 DefaultFilterInvocationSecurityMetadataSource,该类的主要功能就是通过当前的请求地址,获取该地址需要的⽤户⾓⾊。

FilterInvocationSecurityMetadataSource 接⼝是⼀个空接⼝,没有任何⽅法,它继承⾃接⼝ SecurityMetadataSource,接⼝SecurityMetadataSource 的源码如下:public interface SecurityMetadataSource extends AopInfrastructureBean {Collection<ConfigAttribute> getAttributes(Object var1) throws IllegalArgumentException;Collection<ConfigAttribute> getAllConfigAttributes();boolean supports(Class<?> var1);}⽤法⽰例:@Componentpublic class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {@AutowiredMenuService menuService;// AntPathMatcher 是⼀个正则匹配⼯具AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {String requestUrl = ((FilterInvocation) o).getFullRequestUrl();List<Menu> menus = menuService.getAllMenusWithRole();for (Menu menu: menus){if (antPathMatcher.match(menu.getUrl(), requestUrl)){List<Role> roles = menu.getRoles();String[] str = new String[roles.size()];for (int i=0; i<roles.size(); i++){str[i] = roles.get(i).getName();}return SecurityConfig.createList(str);}}// 没有匹配上的,只要登录之后就可以访问,这⾥“ROLE_LOGIN”只是⼀个标记,有待进⼀步处理。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

DelegatingFilterProxy源码分析
说明该方法会在DelegatingFilterProxy初始化的时候调用,目的是创建一个链式调用的Filter类型是FilterChainProxy该类实际上是Filter的一个实现类。

FilterChainProxy源码分析
①FilterInvocation是Spring的一个不同的Bean该方方法中实际上就上将ServletRequest和
ServletResponse转换为HttpServletRequest和HttpServletResponse。

②重点看一下getFilters方法。

③如果该请求下没有相应的Security过滤器,就直接放行
④创建一个VertualFilterChain该Chain作为FilterChain下的一个子Chain主要是实现
SpringSecurity相关Filter的链式调用。

接下来看看VertualFilterChain的doFilter调用代码:
⑤调用VirtualFilterChain的doFilter方法,调用参数参数链式调用。

核心过滤器介绍SecurityContextPersistenceFilter
该Filter主要是通过SecurityContext作为SpringSecurity执行的上下文,这里一般会保存用的的登录的Authentication信息。

而且SpringSecurity会在请求路由的时候将SecurityContext作为HttpSession的一个命名属性存储。

同样SpringSecurty会通过SecurityContextHolder类来方便的获取SecurityContext里面的信息。

在每一次请求结束的时候,该filter负责清空SecurityContextHolder里面的内容。

SpringSecurity建议为了安全考虑,不建议用户直接的去访问HttpSession。

最简单直接的做法就是直接操作SecurityContextHolder类。

注意:这里如果在多个request并发访问的时候,会出现多个Request中从Session获取的是同一个SecurityContext,此时可能会出现一个SecurityContext被多个线程共同访问。

此时为了避免多线程之间由于访问的一个session获取的同一个SecurityContext修改造成的影响,建议用户可以自己创建一个使用SecurityContextHolder的createEmptyContext方法创建一个新的临时的对象为每一个请求。

并且将用户的认证信息存储到SecurityContext中,这样就不会影响其他线程中的数据了。

UsernamePasswordAuthenticationFilter
这个Filter是最常见并且需要定制的Filter之一。

通常需要用户覆盖率比较高。

该Filter在SecurityContextPersistentFilter之后被调用。

该Filter继承自AbstractAuthenticationProcessingFilter。

一般在继承AbstractAuthenticationProcessingFilter的时候只需要覆盖其attemptAuthentication方法。

下面看看源码分析
看看该方法内部实现:
如果是登陆认证URl就执行
填充用户登录认证信息代码:
该Filter获取ProviderManager该类负责查询用户的DB信息以及角色相关信息并返回Authentication对象。

下面看一下ProviderManager对象的authenticate方法。

查看DAOAuthenticationProvider方法:
查看UserDetailService#loadUserbyName方法
DAOAuthenticationProvider调用UserService结束后需要对用户的状态做校验。

相关文档
最新文档