spring security自定义指南
序
本文主要研究一下几种自定义spring security的方式
主要方式
- 自定义UserDetailsService
- 自定义passwordEncoder
- 自定义filter
- 自定义AuthenticationProvider
- 自定义AccessDecisionManager
- 自定义securityMetadataSource
- 自定义access访问控制
- 自定义authenticationEntryPoint
- 自定义多个WebSecurityConfigurerAdapter
自定义UserDetailsService
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//......
@Bean
@Override
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("demoUser1").password("123456")
.authorities("ROLE_USER","read_x").build());
manager.createUser(User.withUsername("admin").password("123456")
.authorities("ROLE_ADMIN").build());
return manager;
}
}
通过重写userDetailsService()方法自定义userDetailsService。这里展示的是InMemoryUserDetailsManager。
spring security内置了JdbcUserDetailsManager,可以自行扩展
自定义passwordEncoder
自定义密码的加密方式,实例如下
//......
@Bean
public DaoAuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
}" title="" data-original-title="复制">
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
<span class="hljs-comment">//......</span>
<span class="hljs-meta">@Bean</span>
public <span class="hljs-type">DaoAuthenticationProvider</span> authenticationProvider() {
<span class="hljs-keyword">final</span> <span class="hljs-type">DaoAuthenticationProvider</span> authProvider = <span class="hljs-keyword">new</span> <span class="hljs-type">DaoAuthenticationProvider</span>();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
<span class="hljs-keyword">return</span> authProvider;
}
<span class="hljs-meta">@Bean</span>
public <span class="hljs-type">PasswordEncoder</span> encoder() {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-type">BCryptPasswordEncoder</span>(<span class="hljs-number">11</span>);
}
}
自定义filter
自定义filter离不开对spring security内置filter的顺序的认知:
Standard Filter Aliases and Ordering
spring security内置的各种filter顺序如下:
Alias | Filter Class | Namespace Element or Attribute |
---|---|---|
CHANNEL_FILTER | ChannelProcessingFilter | http/intercept-url@requires-channel |
SECURITY_CONTEXT_FILTER | SecurityContextPersistenceFilter | http |
CONCURRENT_SESSION_FILTER | ConcurrentSessionFilter | session-management/concurrency-control |
HEADERS_FILTER | HeaderWriterFilter | http/headers |
CSRF_FILTER | CsrfFilter | http/csrf |
LOGOUT_FILTER | LogoutFilter | http/logout |
X509_FILTER | X509AuthenticationFilter | http/x509 |
PRE_AUTH_FILTER | AbstractPreAuthenticatedProcessingFilter Subclasses | N/A |
CAS_FILTER | CasAuthenticationFilter | N/A |
FORM_LOGIN_FILTER | UsernamePasswordAuthenticationFilter | http/form-login |
BASIC_AUTH_FILTER | BasicAuthenticationFilter | http/http-basic |
SERVLET_API_SUPPORT_FILTER | SecurityContextHolderAwareRequestFilter | http/@servlet-api-provision |
JAAS_API_SUPPORT_FILTER | JaasApiIntegrationFilter | http/@jaas-api-provision |
REMEMBER_ME_FILTER | RememberMeAuthenticationFilter | http/remember-me |
ANONYMOUS_FILTER | AnonymousAuthenticationFilter | http/anonymous |
SESSION_MANAGEMENT_FILTER | SessionManagementFilter | session-management |
EXCEPTION_TRANSLATION_FILTER | ExceptionTranslationFilter | http |
FILTER_SECURITY_INTERCEPTOR | FilterSecurityInterceptor | http |
SWITCH_USER_FILTER | SwitchUserFilter | N/A |
内置的认证filter
- UsernamePasswordAuthenticationFilter
参数有username,password的,走UsernamePasswordAuthenticationFilter,提取参数构造UsernamePasswordAuthenticationToken进行认证,成功则填充SecurityContextHolder的Authentication
- BasicAuthenticationFilter
header里头有Authorization,而且value是以Basic开头的,则走BasicAuthenticationFilter,提取参数构造UsernamePasswordAuthenticationToken进行认证,成功则填充SecurityContextHolder的Authentication
- AnonymousAuthenticationFilter
给没有登陆的用户,填充AnonymousAuthenticationToken到SecurityContextHolder的Authentication
定义自己的filter
可以像UsernamePasswordAuthenticationFilter或者AnonymousAuthenticationFilter继承GenericFilterBean,或者像BasicAuthenticationFilter继承OncePerRequestFilter。
关于GenericFilterBean与OncePerRequestFilter的区别可以见这篇spring mvc中的几类拦截器对比
自定义filter主要完成功能如下:
- 提取认证参数
- 调用认证,成功则填充SecurityContextHolder的Authentication,失败则抛出异常
实例
private final AuthenticationManager authenticationManager;
public DemoAuthFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
String token = httpServletRequest.getHeader("app_token");
if(StringUtils.isEmpty(token)){
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "invalid token");
return ;
}
try {
Authentication auth = authenticationManager.authenticate(new WebToken(token));
SecurityContextHolder.getContext().setAuthentication(auth);
filterChain.doFilter(servletRequest, servletResponse);
} catch (AuthenticationException e) {
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
}
}
}" title="" data-original-title="复制">
public class DemoAuthFilter extends GenericFilterBean {
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">AuthenticationManager</span> authenticationManager;
public <span class="hljs-type">DemoAuthFilter</span>(<span class="hljs-type">AuthenticationManager</span> authenticationManager) {
<span class="hljs-keyword">this</span>.authenticationManager = authenticationManager;
}
<span class="hljs-meta">@Override</span>
public void doFilter(<span class="hljs-type">ServletRequest</span> servletRequest, <span class="hljs-type">ServletResponse</span> servletResponse, <span class="hljs-type">FilterChain</span> filterChain) <span class="hljs-keyword">throws</span> <span class="hljs-type">IOException</span>, <span class="hljs-type">ServletException</span> {
<span class="hljs-type">HttpServletRequest</span> httpServletRequest = (<span class="hljs-type">HttpServletRequest</span>) servletRequest;
<span class="hljs-type">HttpServletResponse</span> httpServletResponse = (<span class="hljs-type">HttpServletResponse</span>) servletResponse;
<span class="hljs-type">String</span> token = httpServletRequest.getHeader(<span class="hljs-string">"app_token"</span>);
<span class="hljs-keyword">if</span>(<span class="hljs-type">StringUtils</span>.isEmpty(token)){
httpServletResponse.sendError(<span class="hljs-type">HttpServletResponse</span>.<span class="hljs-type">SC_UNAUTHORIZED</span>, <span class="hljs-string">"invalid token"</span>);
<span class="hljs-keyword">return</span> ;
}
<span class="hljs-keyword">try</span> {
<span class="hljs-type">Authentication</span> auth = authenticationManager.authenticate(<span class="hljs-keyword">new</span> <span class="hljs-type">WebToken</span>(token));
<span class="hljs-type">SecurityContextHolder</span>.getContext().setAuthentication(auth);
filterChain.doFilter(servletRequest, servletResponse);
} <span class="hljs-keyword">catch</span> (<span class="hljs-type">AuthenticationException</span> e) {
httpServletResponse.sendError(<span class="hljs-type">HttpServletResponse</span>.<span class="hljs-type">SC_UNAUTHORIZED</span>, e.getMessage());
}
}
}
设置filter顺序
上面定义完filter之后,然后就要将它放置到filterChain中
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//......
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new DemoAuthFilter(authenticationManager()), BasicAuthenticationFilter.class);
http.csrf().disable();
http.logout().disable();
http.sessionManagement().disable();
}
}
这里把他添加在BasicAuthenticationFilter之前,当然可以根据情况直接替换UsernamePasswordAuthenticationFilter
http.addFilterAt(new DemoAuthFilter(authenticationManager()),UsernamePasswordAuthenticationFilter.class);
自定义AuthenticationProvider
AuthenticationManager接口有个实现ProviderManager相当于一个provider chain,它里头有个List<AuthenticationProvider> providers,通过provider来实现认证。
toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
//......
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
}
catch (InternalAuthenticationServiceException e) {
prepareException(e, authentication);
throw e;
}
catch (AuthenticationException e) {
lastException = e;
}
}
//......
}" title="" data-original-title="复制"></span>
</div>
</div><pre class="hljs java"><code><span class="hljs-function"><span class="hljs-keyword">public</span> Authentication <span class="hljs-title">authenticate</span><span class="hljs-params">(Authentication authentication)</span>
<span class="hljs-keyword">throws</span> AuthenticationException </span>{
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = <span class="hljs-keyword">null</span>;
Authentication result = <span class="hljs-keyword">null</span>;
<span class="hljs-keyword">boolean</span> debug = logger.isDebugEnabled();
<span class="hljs-keyword">for</span> (AuthenticationProvider provider : getProviders()) {
<span class="hljs-keyword">if</span> (!provider.supports(toTest)) {
<span class="hljs-keyword">continue</span>;
}
<span class="hljs-comment">//......</span>
<span class="hljs-keyword">try</span> {
result = provider.authenticate(authentication);
<span class="hljs-keyword">if</span> (result != <span class="hljs-keyword">null</span>) {
copyDetails(authentication, result);
<span class="hljs-keyword">break</span>;
}
}
<span class="hljs-keyword">catch</span> (AccountStatusException e) {
prepareException(e, authentication);
<span class="hljs-comment">// SEC-546: Avoid polling additional providers if auth failure is due to</span>
<span class="hljs-comment">// invalid account status</span>
<span class="hljs-keyword">throw</span> e;
}
<span class="hljs-keyword">catch</span> (InternalAuthenticationServiceException e) {
prepareException(e, authentication);
<span class="hljs-keyword">throw</span> e;
}
<span class="hljs-keyword">catch</span> (AuthenticationException e) {
lastException = e;
}
}
<span class="hljs-comment">//......</span>
}</code></pre>
AuthenticationProvider通过supports方法来标识它是否能够处理这个类型的Authentication。
AnonymousAuthenticationFilter构造的是AnonymousAuthenticationToken,由AnonymousAuthenticationProvider来处理
authentication) {
return (AnonymousAuthenticationToken.class.isAssignableFrom(authentication));
}
} " title="" data-original-title="复制">
public class AnonymousAuthenticationProvider implements AuthenticationProvider,
MessageSourceAware {
//......
public boolean supports(Class<?> authentication) {
return (AnonymousAuthenticationToken.class.isAssignableFrom(authentication));
}
}
UsernamePasswordAuthenticationFilter,BasicAuthenticationFilter构造的是UsernamePasswordAuthenticationToken,由DaoAuthenticationProvider(其父类为AbstractUserDetailsAuthenticationProvider)来处理
authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
} " title="" data-original-title="复制">
public abstract class AbstractUserDetailsAuthenticationProvider implements
AuthenticationProvider, InitializingBean, MessageSourceAware {
//......
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
像上面我们自定义了WebToken,其实例如下:
可以实现Authentication接口,或者继承AbstractAuthenticationToken
private final String token;
public WebToken(String token) {
super(null);
this.token = token;
}
@Override
public Object getCredentials() {
return this.token;
}
@Override
public Object getPrincipal() {
return null;
}
}" title="" data-original-title="复制">
public class WebToken extends AbstractAuthenticationToken {
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> token;
public <span class="hljs-type">WebToken</span>(<span class="hljs-type">String</span> token) {
<span class="hljs-keyword">super</span>(<span class="hljs-literal">null</span>);
<span class="hljs-keyword">this</span>.token = token;
}
<span class="hljs-meta">@Override</span>
public <span class="hljs-type">Object</span> getCredentials() {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.token;
}
<span class="hljs-meta">@Override</span>
public <span class="hljs-type">Object</span> getPrincipal() {
<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}
}
这里就自定义一下支持这类WebToken的AuthenticationProvider
AuthenticationProvider要实现的功能就是根据参数来校验是否可以登录通过,不通过则抛出异常;通过则获取其GrantedAuthority填充到authentication中
如果是继承了AbstractAuthenticationToken,则是填充其authorities属性
前面自定义的DemoAuthFilter会在登陆成功之后,将authentication写入到SecurityContextHolder的context中
可以实现AuthenticationProvider接口,或者继承AbstractUserDetailsAuthenticationProvider(默认集成了preAuthenticationChecks以及postAuthenticationChecks
)
authenticationClass) {
return return (WebToken.class
.isAssignableFrom(authenticationClass));
}
}" title="" data-original-title="复制">
@Service
public class MyAuthProvider implements AuthenticationProvider {
//...
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//......
}
@Override
public boolean supports(Class<?> authenticationClass) {
return return (WebToken.class
.isAssignableFrom(authenticationClass));
}
}
自定义AccessDecisionManager
前面有filter处理了登录问题,接下来是否可访问指定资源的问题就由FilterSecurityInterceptor来处理了。而FilterSecurityInterceptor是用了AccessDecisionManager来进行鉴权。
AccessDecisionManager的几个实现:
- AffirmativeBased(
spring security默认使用
)
只要有投通过(ACCESS_GRANTED)票,则直接判为通过。如果没有投通过票且反对(ACCESS_DENIED)票在1个及其以上的,则直接判为不通过。
- ConsensusBased(
少数服从多数
)
通过的票数大于反对的票数则判为通过;通过的票数小于反对的票数则判为不通过;通过的票数和反对的票数相等,则可根据配置allowIfEqualGrantedDeniedDecisions(默认为true)进行判断是否通过。
- UnanimousBased(
反对票优先
)
无论多少投票者投了多少通过(ACCESS_GRANTED)票,只要有反对票(ACCESS_DENIED),那都判为不通过;如果没有反对票且有投票者投了通过票,那么就判为通过.
实例
其自定义方式之一可以参考聊聊spring security的role hierarchy,展示了如何自定义AccessDecisionVoter。
自定义securityMetadataSource
主要是通过ObjectPostProcessor来实现自定义,具体实例可参考spring security动态配置url权限
自定义access访问控制
对authorizeRequests的控制,可以使用permitAll,anonymous,authenticated,hasAuthority,hasRole等等
.antMatchers("/login","/css/**", "/js/**","/fonts/**","/file/**").permitAll()
.antMatchers("/anonymous*").anonymous()
.antMatchers("/session").authenticated()
.antMatchers("/login/impersonate").hasAuthority("ROLE_ADMIN")
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/auth/*").hasAnyRole("ADMIN","USER")
这些都是利用spring security内置的表达式。像hasAuthority等,他们内部还是使用access方法来实现的。因此我们也可以直接使用access,来实现最大限度的自定义。
实例
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login/**","/logout/**")
.permitAll()
.anyRequest().access("@authService.canAccess(request,authentication)");
}
}" title="" data-original-title="复制">
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">protected</span> void configure(<span class="hljs-type">HttpSecurity</span> http) <span class="hljs-keyword">throws</span> <span class="hljs-type">Exception</span> {
http
.authorizeRequests()
.antMatchers(<span class="hljs-string">"/login/**"</span>,<span class="hljs-string">"/logout/**"</span>)
.permitAll()
.anyRequest().access(<span class="hljs-string">"@authService.canAccess(request,authentication)"</span>);
}
}
这个就有点像使用spring EL表达式,实现实例如下
public boolean canAccess(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
if(principal == null){
return false;
}
if(authentication instanceof AnonymousAuthenticationToken){
//check if this uri can be access by anonymous
//return
}
Set<String> roles = authentication.getAuthorities()
.stream()
.map(e -> e.getAuthority())
.collect(Collectors.toSet());
String uri = request.getRequestURI();
//check this uri can be access by this role
return true;
}
}" title="" data-original-title="复制">
@Component
public class AuthService {
<span class="hljs-keyword">public</span> <span class="hljs-built_in">boolean</span> canAccess(HttpServletRequest request, Authentication authentication) {
<span class="hljs-built_in">Object</span> principal = authentication.getPrincipal();
<span class="hljs-keyword">if</span>(principal == <span class="hljs-literal">null</span>){
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
<span class="hljs-keyword">if</span>(authentication <span class="hljs-keyword">instanceof</span> AnonymousAuthenticationToken){
<span class="hljs-comment">//check if this uri can be access by anonymous</span>
<span class="hljs-comment">//return</span>
}
Set<<span class="hljs-built_in">String</span>> roles = authentication.getAuthorities()
.stream()
.map(e -> e.getAuthority())
.collect(Collectors.toSet());
<span class="hljs-built_in">String</span> uri = request.getRequestURI();
<span class="hljs-comment">//check this uri can be access by this role</span>
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
}
自定义authenticationEntryPoint
比如你想给basic认证换个realmName,除了再spring security配置中指定
security.basic.realm=myrealm
也可以这样
public static BasicAuthenticationEntryPoint createBasicAuthEntryPoint(String realmName){
BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName(realmName);
return entryPoint;
}" title="" data-original-title="复制"></span>
</div>
</div><pre class="hljs typescript"><code> httpBasic().authenticationEntryPoint(createBasicAuthEntryPoint(<span class="hljs-string">"myrealm"</span>))
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> BasicAuthenticationEntryPoint createBasicAuthEntryPoint(<span class="hljs-built_in">String</span> realmName){
BasicAuthenticationEntryPoint entryPoint = <span class="hljs-keyword">new</span> BasicAuthenticationEntryPoint();
entryPoint.setRealmName(realmName);
<span class="hljs-keyword">return</span> entryPoint;
}</code></pre>
自定义多个WebSecurityConfigurerAdapter
spring security使用antMatchers不支持not的情况,因此可以自定义多个WebSecurityConfigurerAdapter,利用order优先级来实现匹配的覆盖,具体可以参考这篇文章Multiple Entry Points in Spring Security
小结
还有其他自定义的方式,等后续有发现再补上。
doc
- Spring Security password hashing example
- spring mvc中的几类拦截器对比
- spring security 自定义认证
- Spring Security Tutorial
- Security with Spring
- 话说Spring Security权限管理(源码)
原文地址:https://segmentfault.com/a/1190000012560773
spring security自定义指南的更多相关文章
- Spring Security 自定义登录认证(二)
一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Se ...
- (二)spring Security 自定义登录页面与校验用户
文章目录 配置 security 配置下 MVC 自定义登录页面 自定义一个登陆成功欢迎页面 效果图 小结: 使用 Spring Boot 的快速创建项目功能,勾选上本篇博客需要的功能:web,sec ...
- 解决Spring Security自定义filter重复执行问题
今天做项目的时候,发现每次拦截器日志都会打两遍,很纳闷,怀疑是Filter被执行了两遍.结果debug之后发现还真是!记录一下这个神奇的BUG! 问题描述 项目中使用的是Spring-security ...
- 【JavaEE】SSH+Spring Security自定义Security的部分处理策略
本文建立在 SSH与Spring Security整合 一文的基础上,从这篇文章的example上做修改,或者从 配置了AOP 的example上做修改皆可.这里主要补充我在实际使用Spring Se ...
- Spring Security自定义GrantedAuthority前缀
如果我们正使用Spring Security提供默认的基于方法的权限认证注解如下: @PreAuthorize("hasAnyRole('ADMIN', 'USER')") pub ...
- Spring Boot整合Spring Security自定义登录实战
本文主要介绍在Spring Boot中整合Spring Security,对于Spring Boot配置及使用不做过多介绍,还不了解的同学可以先学习下Spring Boot. 本demo所用Sprin ...
- Spring Security 自定义 登陆 权限验证
转载于:https://www.jianshu.com/p/6b8fb59b614b 项目简介 基于Spring Cloud 的项目,Spring Cloud是在Spring Boot上搭建的所以按照 ...
- 02 spring security 自定义用户认证流程
1. 自定义登录页面 (1)首先在static目录下面创建login.html 注意: springboot项目默认可以访问resources/resources, resources/s ...
- Spring Security 自定义登录页面
SpringMVC + Spring Security,自定义登录页面登录验证 学习参考:http://www.mkyong.com/spring-security/spring-security-f ...
随机推荐
- [java]java构造器 标签: java面向对象 2017-06-11 11:16 195人阅读 评论(12)
构造器这个概念,各种语言都有出现,虽然为了考试或者其他学了好多遍,但是自己一直不能理解这个概念,前几天又学了一遍,突然就明白了,下面随小编来一起学习一下吧. 什么是构造器? 在类别基础的面向对象程序设 ...
- 一.JDBC学习入门
一.JDBC相关概念介绍 1.1.数据库驱动 这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡 ...
- 如何用好消息推送(push)做APP运营
作为移动端APP产品运营最重要的运营手段,消息推送(push)被越来越多的APP厂商所重视,在信息泛滥的移动互联网时代,手机APP应用安装得越来越多,小小的手机屏幕每天收到的消息推送也越来越多,站在用 ...
- QT_OPENGL-------- 2.shader
用可编程管线绘制一个三角形 1.以上一节window为基准,进行绘制. 2.下载编译glew,并在.pro添加动态链接,并在头文件中引用. LIBS +=-L/usr/lib64 -lGLEW 可能根 ...
- Android中Scroller类的分析
今天看了一下项目中用到的ViewFlow控件,想弄明白其工作原理.从头开始分析,卡在"滚动"这儿了. 做android也快两年了,连最基本的滚动都不熟悉,真是惭愧...遂网上找资料 ...
- 【LINUX】降级安装低版本GCC,G++
由于要制作crosstool,需要用到gcc 4.1.2来编译,而Ubuntu 12.04下的gcc版本是gcc 4.6.3,高版本的gcc也不是好事啊. 下面介绍gcc 4.1.2的编译安装方法: ...
- 在 Linux 启动或重启时执行命令与脚本
有时可能会需要在重启时或者每次系统启动时运行某些命令或者脚本.我们要怎样做呢?本文中我们就对此进行讨论. 我们会用两种方法来描述如何在 CentOS/RHEL 以及 Ubuntu 系统上做到重启或者系 ...
- 数组的查找,删除 Day07
package com.sxt.arraytest2; /* * 形参列表的作用:1.接受方法调用处传来的实参 * 2.规定了实参传入数据的类型 */ import java.util.Arrays; ...
- oralce CUBE
select id,area,stu_type,sum(score) score from students group by cube(id,area,stu_type) order by id,a ...
- Gym - 101620H_Hidden Hierarchy(树+模拟)
Hidden Hierarchy 题目链接 题目描述 You are working on the user interface for a simple text-based file explor ...