加入如下依赖
<!--Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
创建一个测试Controller如下

启动项目后浏览器访问 http://localhost:8080/hello
提示登录
application.yml 配置 security的 name 与 password

登录后即可正常访问

管理员,同事具有 ADMIN,USER权限,可以访问所有资源
普通用户,只能访问 /product/**
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
// 管理员,同事具有 ADMIN,USER权限,可以访问所有资源
.withUser("admin1").password(new BCryptPasswordEncoder().encode("admin1")).roles("ADMIN","USER")
.and().passwordEncoder(new BCryptPasswordEncoder())
// 普通用户,只能访问 /product/**
.withUser("user1").password(new BCryptPasswordEncoder().encode("user1")).roles("USER");
}
管理员:ADMIN
用户:USER
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity.authorizeRequests().antMatchers("/product/**").hasAnyRole("USER")
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest().authenticated().and().formLogin().and().httpBasic();
}
通过SecurityContextHolder来获取用户信息
@RequestMapping("/info")
@ResponseBody
public String product(){
//控制器获取当前登录用户
String currentUser = "";
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if(principal instanceof UserDetails){
currentUser = ((UserDetails) principal).getUsername();
}else {
currentUser = principal.toString();
}
return "配太华以为刀兮、长四读以为佩. ----"+currentUser;
}
spring security核心组件有:
SecurityContext、SecurityContextHolder、Authentication、Userdetails 和 AuthenticationManager等
安全上下文,用户通过Spring Security 的校验之后,验证信息存储在SecurityContext中,SecurityContext的接口定义如下:
定义了两个方法,实际上其主要作用就是获取Authentication对象

是一个持有者,用来持有住SecurityContext实例的
在Spring Security中,在请求之间存储SecurityContext的责任落在SecurityContextPersistenceFilter上,默认情况下,该上下文将上下文存储为HTTP请求之间的HttpSession属性。它会为每个请求恢复上下文SecurityContextHolder,并且最重要的是,在请求完成时清除SecurityContextHolder。SecurityContextHolder是一个类,他的功能方法都是静态的(static)。
SecurityContextHolder可以设置指定JVM策略(SecurityContext的存储策略)
这个策略有三种:
MODE_THREADLOCAL:SecurityContext 存储在线程中。
MODE_INHERITABLETHREADLOCAL:SecurityContext 存储在线程中,但子线程可以获取到父线程中的 SecurityContext。
MODE_GLOBAL:SecurityContext 在所有线程中都相同。
SecurityContextHolder默认使用MODE_THREADLOCAL模式,即存储在当前线程中。
//其作用就是存储当前认证信息
SecurityContextHolder.getContext().setAuthentication(token);
一组用户名密码信息.

该接口有四个get方法,分别表示
/*
*获取用户权限,一般情况下获取到的是用户的角色信息。
*/
getAuthorities:
/*
*获取证明用户认证的信息,通常情况下获取到的是密码等信息。
*/
getCredentials:
/*
*获取用户的额外信息,(这部分信息可以是我们的用户表中的信息)。
*/
getDetails:
/*
*获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是
* UserDetails (UserDetails也是一个接口,里边的方法有getUsername,getPassword等)。
*/
getPrincipal:
/*
*获取当前 Authentication 是否已认证。
*/
isAuthenticated:
/*
*设置当前 Authentication 是否已认证(true or false)。
*/
setAuthenticated:
存储用户信息

方法含义如下
/*
*获取用户权限,本质上是用户的角色信息。
*/
getAuthorites
/*
*获取密码。
*/
getPassword
/*
*获取用户名。
*/
getUserName
/*
*账户是否过期。
*/
isAccountNonExpired
/*
*账户是否被锁定。
*/
isAccountNonLocked
/*
*密码是否过期。
*/
isCredentialsNonExpired
/*
*账户是否可用。
*/
isEnabled
UserDetailsService也是一个接口,且只有一个方法loadUserByUsername,他可以用来获取UserDetails。

通常会定义一个CustomUserDetailsService来实现UserDetailsService接口,并实现其loadUserByUsername方法。在实现loadUserByUsername方法的时候,可以通过查询数据库(或者是缓存、或者是其他的存储形式)来获取用户信息,然后组装成一个UserDetails,并返回。
AuthenticationManager 是一个接口,它只有一个方法,接收参数为Authentication

AuthenticationManager 的作用就是校验Authentication,
如果验证失败会抛出AuthenticationException异常。
Spring Security 在 Filter 中创建 Authentication 对象,并调用 AuthenticationManager 进行校验.
spring security 维护了一个filter chain
chain中的每一个filter都具有特定的责任,并根据所需的服务在配置总添加。
filter的顺序很重要,因为他们之间存在依赖关系。
pring security中有如下filter(按顺序的):
1.ChannelProcessingFilter:因为它可能需要重定向到不同的协议
2.SecurityContextPersistenceFilter:可以在web请求开头的SecurityContextHolder中设置SecurityContext:并且SecurityContext的任何更改都可以复制到HttpSession当web请求结束时准备好与下一个web请求一起使用
3.ConcurrentSessionFilter:身份验证处理-UsernamePasswordAuthenticationFilter,CasAuthenticationFilter,BasicAuthenticationFilter等。以便SecurityContextHolder可以修改为包含有效的Authentication请求令牌
4.SecurityContextHolderAwareRequestFilter:包装请求对象request
5.JaasApiIntegrationFilter
6.RememberMeAuthenticationFilter:记住我服务处理
7.AnonymousAuthenticationFilter:匿名身份处理,更新SecurityContextHolder
8.ExceptionTranslationFilter:获任何Spring Security异常,以便可以返回HTTP错误响应或启动适当的AuthenticationEntryPoint
9.FilterSecurityInterceptor:用于保护web URI并在访问被拒绝时引发异常
DelegatingFilterProxy是一个特殊的filter,存在于spring-web模块中。DelegatingFilterProxy通过继承GenericFilterBean使得自己变为了一个Filter(因为GenericFilterBean implements Filter)。它是一个Filter,其命名却以proxy结尾。
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
此配置是我们使用web.xml配置Filter时做法。但是与普通的Filter不同的是DelegatingFilterProxy并没有实际的过滤逻辑,他会尝试寻找filter-name节点所配置的myFilter,并将过滤行为委托给myFilter来处理。这种方式能够利用Spring丰富的依赖注入工具和生命周期接口,因此DelegatingFilterProxy提供了web.xml与应用程序上下文之间的链接。
spring security入口
在没有spring boot之前,我们要使用spring security的话,通常在web.xml中添加如下配置:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这里配置的是DelegatingFilterProxy。它实际上会去找到filter-name节点中的Filter——springSecurityFilterChain,并将实际的过滤工作交给springSecurityFilterChain处理。
在使用spring boot之后,这一xml配置被Java类配置给代替了。@EnableWebSecurity 注解,通过跟踪源码可以发现@EnableWebSecurity会加载WebSecurityConfiguration类,而WebSecurityConfiguration类中就有创建springSecurityFilterChain这个Filter的代码:

spring security 的核心是基于filter
入口filter是springSecurityFilterChain(它会被DelegatingFilterProxy委托来执行过滤任务)
springSecurityFilterChain实际上是FilterChainProxy (一个filter)
FilterChainProxy里边有一个SecurityFilterChain集合,doFIlter的时候会从其中取。
准备:整合mybatis连接本地数据库

/**
* ID
*/
private Integer id;
/**
* 账号
*/
private String name;
/**
* 密码
*/
private String password;
/**
* 性别
*/
private String sex;
查询用户角色以及设置角色、即上面的userFromDatabase.getRole()通常是一个list、所以设置角色的时候,就是for循环new 多个SimpleGrantedAuthority并设置。
@Component("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
@Autowired
private Userservice userservice;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1. 查询用户
User user = userservice.getOne(username);
// boolean s = PASSWORD_ENCODER.matches("123156", "$2a$10$69twjsLqHXDIxObXBTqV6uUrkp0UgglnTjPbQKSZyQibTq4VRKgsG");
if (user==null){
throw new UsernameNotFoundException("User " + username + " was not found in db");
//这里找不到必须抛异常
}
// 2. 设置角色
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(user.getSex());
grantedAuthorities.add(grantedAuthority);
return new org.springframework.security.core.userdetails.User(username,user.getPassword(), grantedAuthorities);
}
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)// 设置自定义的userDetailsService
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
spring security 可以准确控制session何时创建以及Spring Security如何与之交互:
always 没有session就创建。
ifRequired (default),如果需要就创建(默认)。
never
stateless 不创建不使用session
需要在SecurityConfiguration中修改为stateless状态
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..