赞
踩
目录
6.Spring中的IOC和AOP 是什么含义? 它们在项目中的作用是什么?举例说明?
10. 简述Spring的事务有几种管理方法,写出一种配置方式?
12. 请说明Spring事务管理中的传播行为和数据库事务的隔离级别?
16. Spring Security有哪几种常见的认证方式?
17 .怎么通过spring security创建自定义的权限用户?
18. 怎么开启SpringSecurity安全机制? 怎么编写自定义的简单安全配置?
19. 怎么给通过Spring Security给密码加密?
20. 怎么用Spring security防止跨域的csrf攻击?
21. Spring Security里面自带的角色权限是怎么样的?是怎么配置的?
22. Spring Security怎么定义保护路径的配置方法?
23. Spring Security的核心分为哪几个部分?
答: 1) Spring 是一个开源的轻量级应用开发框架,目的在于简化企业开发。
2) Spring 提供IOC和AOP应用,可以将组建的耦合度降到最低,有利于应用后期的维护和升级。
3) Spring 提供一个整体的解决方案,有助于开发者技术选型,可以与第三方框架整合应用。
答: 1) 可以通过<bean>定义的scope属性指定Bean的作用域或者使用@Scope注解指定Bean的作用域。
2) 默认Bean的作用域是singleton。用@Scope注解来结合@Bean注解可以定义该Bean的作用域,如: prototype为每个请求创建一个实例。
- @Bean({"b1","b2"})
- @Scope("prototype")
- public MyBean myBean(){
- return new MyBean();
- }
3)Bean的5种作用域分别为: singleton,prototype,request,session, global session。
@Component, @Repository, @Service, @Scope, @Autowire,@Inject,@Value
答:
1) @Component为组件通用注解。
2) @Repository 为持久层注解。
3) @Service 为业务层注解。
4) @Scope为Bean的作用域注解。
5) @Autowire、@Inject为指定Bean之间的依赖关系。
6) @Value 为Spring 表达式的值注解。
答: SpringMVC的工作流程如下:
1) 浏览器发出请求,请求交给前端控制器Dispatcher Servlet来处理。
2) 控制器通过 HandlerMapping 维护的请求和Controller请求信息,找到相应的Controller组件处理请求。
3) 执行Controller 组件约定方法处理请求,在约定方法可以调用service和dao等组件来完成操作,约定方法可以返回一个ModelAndView 对象,封装了模型数据和视图名称的信息。
4) 中心控制器接收到ModelAndView之后,调用ViewResolver组件,定位View的JSP并传递Model信息,生成响应的界面。
答:
1)与jsp页面交互时要写很多Java代码。
2) 控制器过于灵活,缺少一个公用的控制器。
答:
IOC: 控制反转,一层含义是控制权的转移,由传统在程序中控制依赖转移到由容器来控制程序, 第二层含义是依赖注入,将相互依赖的对象分离,在Spring配置文件中描述他们的依赖关系,他们的依赖关系只在使用的时候建立。
AOP: 面向切面编程,是一种编程思想, OOP的延续,将传统非核心的业务提取出来,进行单独处理。
Spring IOC和AOP在项目中的作用是为了解决系统代码耦合度过高的问题,使代码重用度高,易于维护,比如事务、日志、安全。
答:
1.实例Bean:容器寻找Bean的定义信息并将其实例化,通过doCreateBean()方法实现。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
2.Bean属性注入:使用依赖注入,Spring按照Bean定义信息配置Bean所有属性, 通过populateBean方法,在AbstractAutowireBeanCapableBeanFactory抽象类里实现。
3.设置Bean的名称(id):如果Bean类已经实现org.springframework.beans.BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。
4.设置Bean的工厂:如果Bean类已经实现org.springframework.beans.factory.BeanFactoryAware接口,给Bean设置BeanFactory。
5.准备初始化阶段: BeanPostProcessors的ProcessBeforeInitialization()如果org.springframework.beans.factory.config.
BeanPostProcessors和Bean关联,postProcessBeforeInitialization()方法将被将被调用,为初始化Bean出初始化作准备工作。
6.初始化Bean:如果Bean类已实现org.springframework.beans.factory.InitializingBean接口,则执行它afterProPertiesSet()方法。
7.初始化Bean的方法:可以在Bean定义文件中使用"init-method"属性设定方法名称例如:如果有以上设置的话,则执行到这个阶段,就会执行initBean()方法
8.初始化之后的工作: BeanPostProcessors的postProcessaAfterInitialization(), 如果有任何的BeanPostProcessors实例与Bean实例关联,则执行BeanPostProcessors实例的postProcessaAfterInitialization()方法
答: 3种。
1 ) 基于xml文件的形式。
2) 基于注解的形式。
3) 基于java的形式,configuration 的javaBean, @Bean注解。
答: spring-beans,spring-core,spring-context,spring-expression。
答: Spring 事务有2种方式:
1) 编程式事务: (代码中嵌入)。
2) 声明式事务:(注解,XML)。
注解方式配置事务的方式如下:
首先,需要在applicantionContext.xml文件中添加启用的配置tx: annotation-driven 开启。
-
- <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" mode="proxy"/>
-
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="ds"/>
- </bean>
然后使用 @Transactionnal 注解,代码如下:
- @Transactional
-
- public class A implements B{
-
- //@Transactional
-
- public void insertInfo(Foo foo){}
-
- public void updateInfo(Foo foo){}
-
- }
@Transactional 注解标记可以用在类定义和方法之前,一般使用在方法之前。
答: Spring 中的事务管理是使用AOP代理来实现的,核心实现类是TransactionAspectSupport,对被代理的每个方法进行拦截,在方法执行前启动事务,方法执行完后根据是否有异常和异常的种类进行提交或者回滚。
答: Spring事务的传播行为如下:
1)PROPAGATION_REQUIRED : 支持当前事务,如果当前没有事务,则创建一个事务,这是最常见的选择。
2)PROPAGATION_SUPPORTS : 支持当前事务,如果当前没有事务,就以非事务来执行。
3)PROPAGATION_MANDATORY : 支持当前事务,如果没有当前事务,就抛出异常。
4)PROPAGATION_REQUIRES_NEW : 新建事务,如果当前存在事务,就把当前事务挂起。
5)PROPAGATION_NOT_SUPPORTED : 以非事务执行操作,如果当前存在事务,则当前事务挂起。
6)PROPAGATION_NEVER : 以非事务方式执行,如果当前存在事务,则抛出异常。
7)PROPAGATION_NESTED : 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED 类似的操作。
数据库系统提供了四种事务隔离级别,不同的隔离级别采用不同的锁来实现,在四种事务隔离级别中,Serializable的隔离级别最高,ReadUncommited的隔离级别最低。 大多数数据库默认的隔离级别为ReadCommited, 如SqlServer, 也有少部分数据库的隔离级别为RepeatableRead ,如Mysql。
1) ReadUncommited: 读未提交数据(会出现脏读,不可重复读和幻读)。
2) ReadCommited: 读已提交(会出现不可重复读和幻读)。
3) RepeatableRead: 可重复读(会出现幻读)。
4) Serializable: 串行化。
名词解释:
脏读: 一个事务读到另一个事务未提交的数据。
不可重复读: 在同一个事务中,多次读取同一数据返回的结果有所不同,就是说后续读取可以读取到另外一个事务更新到的数据。
可重复读: 在同一次事务中多次读取数据时,能够保证所读的数据是一样的,后续读取不能读取到其他事务已提交更新的数据。
幻读: 所谓幻读,是指一个事务在读取某条范围的记录时,如果另一个事务再次往该范围插入一条记录,那么之前事务再读取该范围的数据时,会出现幻行。
Innodb 存储引擎通过MVCC 解决了幻读的问题, mysql默认地隔离级别为可重复读。
答: 在web应用程序中,要对Spring的IOC容器(WebApplicationContext)进行初始化,可以通过配置ContextLoadListener监听器实现。
在web.xml文件中添加如下配置即可完成初始化:
-
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:applicationContext.xml</param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
经过以上配置后 ,即可在servlet或JSP中,直接使用Spring提供的WebApplicationContextUtils工具类可以获取Spring的WebApplicationContext容器,然后可以从该容器中获取你想要的bean了 。
答: SSH框架指的是Struts,Spring和Hibernate,其中Spring的作用是控制反转,Stuts作用是流程控制,Hibernate的作用是用于数据持久化。
答: 1.DispatcherServlet: 中央处理器,用来接收并处理客户端的请求。
2.Contoller: 处理请求的控制器。
3.HandlerMapping: 负责中央处理器转给Controller时的映射策略。
4.ModelAndView: 用来给前端返回视图的封装类。
5.ViewResolver: 视图解析器,用来解析ModelAndView的视图。
6.Interceptor: 拦截器,负责我们拦截定义的请求并处理拦截的请求。
答:
1) 基于内存的方式。
2) 基于数据库的方式。此种方式为常用的方式,适用于生产环境。
3) 基于LDAP的方式。
LDAP有配置远程服务器和配置嵌入是的LDAP服务器2种方式,
远程的LDAP服务器需要配置contextSource方法来配置远程服务器的地址url。 .contextSource().url("ldap://habuma.com:389/dc=habuma,dc=com")
嵌入的LDAP服务器需要通过root方法来配置。 .contextSource().root("dc=habuma,dc=com")
答:
我们只需要实现Spring Security提供的UserDetailsService接口就行了。通过实现该接口里的loadUserByUserName来自定义查找用户等操作。另外可以通过SpitterRepository来创建user对象,其中原理是获取到spitter对象,然后用它来创建user对象。我们不需要了解其框架的实现原理,只需要获取到user对象即可。
UserDetailsSerivce基于jdbc源码分析:
SpringSecurity的core包中users.dll文件用来初始化默认的数据库中users表(用户表),authorities(授权表)
- create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null);
- create table authorities (username varchar_ignorecase(50) not null,authority varchar_ignorecase(50) not null,constraint fk_authorities_users foreign key(username) references users(username));
- create unique index ix_auth_username on authorities (username,authority);
可以发现JdbcDaoImpl实现了UserDetailsService 接口,在
loadUserByUsername 方法中主要做了一下几件事:
1) 根据用户名查询Sring自带的数据库中是否存在该用户,如果 不存在,那么抛出异常,如果存在,那么就继续后续的操作。
2) 找到用户后,再去查询该用户是否具有角色权限,有的话就讲过角色权限添加到GrantedAuthority集合中去。
- UserDetails user = (UserDetails)users.get(0);
- Set<GrantedAuthority> dbAuthsSet = new HashSet();
- if (this.enableAuthorities) {
- dbAuthsSet.addAll(this.loadUserAuthorities(user.getUsername()));
- }
-
- if (this.enableGroups) {
- dbAuthsSet.addAll(this.loadGroupAuthorities(user.getUsername()));
- }
3) 最好判断如果有权限的设置,那么就初始化一个UserDetails对象返回出去,传的参数分别为username,password,auth。
- List<GrantedAuthority> dbAuths = new ArrayList(dbAuthsSet);
- this.addCustomAuthorities(user.getUsername(), dbAuths);
- if (dbAuths.size() == 0) {
- this.logger.debug("User '" + username + "' has no authorities and will be treated as 'not found'");
- throw new UsernameNotFoundException(this.messages.getMessage("JdbcDaoImpl.noAuthority", new Object[]{username}, "User {0} has no GrantedAuthority"));
- } else {
- return this.createUserDetails(username, user, dbAuths);
- }
4) 同样的,如果是自定义的类实现了UserDetailService接口,那么查询后添加授权,会返回出一个user对象出去,而我们不需要其具体的实现原理。
答: 通过使用@EnableWebSecurity注解开启web安全机制,如果是mvc应用,那么需要使用@EnableWebMvcSecurity注解来开启web安全机制; 如果想要编写自定义的安全配置文件,那么需要继承Spring Security的WebSecurityConfigurerAdapter类,然后重写configure方法,这样就能实现自定义的安全配置了。
答: 通过.passwordEncoder(new StandardPasswordEncoder("53crt3t"))方法给用户的密码进行转码加密。
还有其他的加密实现类,加密策略为:
BCrptPasswordEncoder, NoOpPasswordEncoder。
另外也可以通过自定义的接口来实现转码:
public interface PasswordEncoder{
String encode(CharSequence rawPassWord);
Boolean matches(CharSequence rawPassWord,String encodedPassword);
}
需要注意的是: 生产中,数据库的密码是永远不会解码的,用户输入的密码会通过相同的算法来比较数据库里的密码,即转码后才作比较。
答: 重写WebSecurityConfigureAdapter里的configure方法,使用HttpSecurity的crsf方法,来防止跨域攻击。.and().crsf().enable();方法来开启crsf防护。
- @Override
-
- protected void configure(HttpSecurity http) throws Exception{
-
- http
-
- ....
-
- .and()
-
- .csrf()
-
- .enable()
-
- }
-
- .and().csrf().disable();
答: 使用 AuthenticationManagerBuilder 类来得到 .roles()方法来指定用户的角色,一般用户是USER,管理员是ADMIN。 通过指定用户角色来限定访问,没有角色的用户是不能访问系统的。
- auth
-
- .inMemoryAuthentication()
-
- .withUser("user").password("password").roles("USER").and()
-
- .withUser("admin").password("password").roles("USER","ADMIN");
.withUser()方法为内存用户存储添加新的用户,参数为username,通过.and()方法可以将多个用户的配置连接在一起。
- @Override
- protected void configure(HttpSecurity Http) throws Exception{
-
- http.
-
- authorizeRequests()
-
- .antMatchers("/spitters/me").authenticated()
-
- .antMatchers(HttpMethod.POST,"/spittles").authenticated()
-
- .anyRequest().permitAll();
-
- }
其中 antMatchers()方法能够同时指定多个路径来进行保护,同时也可以使用通配符类指定路径 "/spitters/**", 也可以使用regexMatchers()方法能够接受正则表达式的方式来保护请求。
如下是常用的保护路径的配置方法:
由三个部分组成: config,core,web。
Autowire 默认由类型来注入,Resource由名字和类型来注入。
使用Autowire时,要保证@service实现类只有一个,如果有多个的话,找子类会报错,可以结合@qualifier("beanName")注解指定特定的bean来使用。
Resource注解的缺点是,会找两次,先会根据名字去找bean, 然后再根据类型再去找bean。
因此在使用的时候需要注意autowire的@service不能有多个接口,@Resource 注解将名字指定清楚即可。
首先我们要了解spring 实例化bean的三步骤:
1) doCreateBeanInstance,通过无参构造方法创建一个bean的实例。
2) populateBean,填充bean的属性。
3) initialBean, 执行bean的初始化。
Spring的循环依赖主要发生在第一步和第二步。
Spring的依赖注入有三种情况:
1. 构造器注入,对于构造器注入形式的循环依赖, Spring框架无法自己解决。
2. 对于prototype 作用域类型的bean,形成的循环依赖,Spring框架也无法解决,因为Spring框架对prototype作用域的bean是不缓存的,无法提前暴露一个创建中的bean解决。
3. 对于setter形式的注入,形成的循环依赖,Spring框架是通过三级缓存解决的。
1) 一级缓存, singletonObjects。
2) 二级缓存, earlySingletonObjects。
3) 三级缓存,singletonFactories, 类型为ObjectFactory。
- /** Cache of singleton objects: bean name --> bean instance */
- private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
-
- /** Cache of singleton factories: bean name --> ObjectFactory */
- private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
- /** Cache of early singleton objects: bean name --> bean instance */
- private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
看一下getSingleton源码:
- protected Object getSingleton(String beanName, boolean allowEarlyReference) {
- Object singletonObject = this.singletonObjects.get(beanName);
- //先直接从一级缓存里拿,如果拿到了就返回
- if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
- synchronized (this.singletonObjects) {
- // 二级缓存中取
- singletonObject = this.earlySingletonObjects.get(beanName);
- if (singletonObject == null && allowEarlyReference) {
- // 从三级缓存中取
- ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
- if (singletonFactory != null) {
- singletonObject = singletonFactory.getObject();
- // 从三级缓存中移到二级缓存
- this.earlySingletonObjects.put(beanName, singletonObject);
- this.singletonFactories.remove(beanName);
- }
- }
- }
- }
- return singletonObject;
- }
通过ObjectFactory将被依赖的bean提前暴露出去,那么在用三级缓存的时候可以直接get出来。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。