介绍
Spring Security是Spring项目组中用来提供安全认证服务的框架,不仅可以基于数据库进行操作,还可以基于配置文件操作
包括两个主要操作:
- 认证
- 授权
使用spring security快速入门步骤
- 导入依赖
<!-- SpringSecurity相关依赖-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
- web.xml文件中配置过滤器链
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring Secutiry 过滤器链,springSecurityFilterChain名称不能变,否则无法被识别 -->
<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>
- spring security核心配置文件配置(spring-security.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!--配置不过滤的资源(静态资源、登录相关)-->
<security:http security="none" pattern="WEB-INF/view/login.ftl"/>
<security:http auto-config="true" use-expressions="true">
<!--
auto-config =true时,会配置十个默认过滤器
use-expressions:
声明为true,那么在access属性要用access="hasRole(‘ROLE_USER‘)"
声明为false(默认),那么access直接就是access="ROLE_USER"
-->
<!--
intercept-url 定义过滤规则
pattern 表示对哪些URL进行权限控制
access 表示在请求对赢得URL需要什么权限
-->
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<!-- 自定义登录页面
authentication-failure-url权限校验失败后跳转页面
default-target-url登录成功后跳转页
登录页面用户名固定为username,密码password,action:login
-->
<security:form-login
login-page='/login'
default-target-url="/welcome"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password"/>
<!--没有权限错误页面-->
<security:access-denied-handler error-page="/nopermit"/>
<!--退出只需要简单配置-->
<security:logout
logout-success-url="/login?logout"/>
<!--CSRF跨服务器的request、forward请求访问,默认开启-->
<security:csrf />
</security:http>
<!--
基于内存的用户认证 InMemoryUserDetailsManager
-->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="memory1" password="123456" authorities="ROLE_USER, ROLE_ADMIN" />
<security:user name="memory2" password="123456" authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
从别人那剽来的工作原理图
-
WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。
-
SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将 SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将 SecurityContextHolder 中的信息清除,例如在Session中维护一个用户的安全信息就是这个过滤器处理的。
-
HeaderWriterFilter:用于将头信息加入响应中。
-
CsrfFilter:用于处理跨站请求伪造。
-
LogoutFilter:用于处理退出登录。
-
UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的usernameParameter 和 passwordParameter 两个参数的值进行修改。
-
DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
-
BasicAuthenticationFilter:检测和处理 http basic 认证。
-
RequestCacheAwareFilter:用来处理请求的缓存。
-
SecurityContextHolderAwareRequestFilter:主要是包装请求对象request。
-
AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在 Authentication 对象,如果不存在为其提供一个匿名 Authentication。
-
SessionManagementFilter:管理 session 的过滤器
-
ExceptionTranslationFilter:处理 AccessDeniedException 和 AuthenticationException 异常。
-
FilterSecurityInterceptor:可以看做过滤器链的出口。
-
RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。
Spring Security登录认证
使用UserDetails和UserDetailsService来认证
- UserDetails:封装当前正在认证的用户信息
- UserDetailsService:规范了进行认证的方法
使用Spring Security之前
的登录流程
- 登录页面提交登录信息
- controller获得登录信息交给service
- service调用dao层完成认证,根据用户名密码查询数据库
- dao层返回用户对象给service
- service将用户对象返回给controller
- controller去判断登录成功失败跳转页面
使用Spring Security登录流程
- 不需要controller层,这一部分由Spring Security完成,在security.xml进行配置
- 让自己的myUserDetailsService类 实现 UserDetailsService接口,重写loadUserByUsername方法,在调用service时就会调用这个接口
- 调用dao层返回我们自己的user对象封装成security提供的user对象
在spring-security.xml配置
<security:authentication-manager>
<security:authentication-provider user-service-ref='myUserDetailsService'/>
</security:authentication-manager>
<!--
<bean id="myUserDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<!--<property name="dataSource" ref="dataSource"/>
</bean>
-->
这个配置中的myUserDetailsService需要自己定义,去扩展UserDetailsService
userService
public interface userService extends UserDetailsService {
}
**userServiceImpl **
@Service("myUserDetailsService")
@Transactional
public interface userServiceImpl implements interface {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = null;
try {
userInfo = userDao.findByUsername(username) ;
} catch (Exception e) {
e.printStackTrace();
}
//security提供的user对象,三个参数分别是 账号、密码、权限
User user = new User(userInfo.getUsername(), userInfo.getPassword(),null );
return user ;
}
}
userDao
public class UserDao {
/**
*返回的对象和数据库表对应,为了和security提供的user对象区分,叫UserUnfo
*/
@Select("select * from users where username=#{username}")
public UserInfo findByUsername(String username) throws Exception;
}
例子1
创建一个web工程
pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<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>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!--配置不过滤的资源(静态资源、登录相关)-->
<security:http security="none" pattern="/login.jsp"/>
<security:http auto-config="true" use-expressions="true">
<!--
auto-config =true时,会配置十个默认过滤器
use-expressions:
声明为true,那么在access属性要用access="hasRole(‘ROLE_USER‘)"
声明为false(默认),那么access直接就是access="ROLE_USER"
-->
<!--
intercept-url 定义过滤规则
pattern 表示对哪些URL进行权限控制
access 表示在请求对赢得URL需要什么权限
-->
<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
<!--没有权限错误页面-->
<security:access-denied-handler error-page="/noauthority.jsp"/>
<!--CSRF跨服务器的request、forward请求访问,默认开启-->
<security:csrf/>
</security:http>
<!--
基于内存的用户认证 InMemoryUserDetailsManager
-->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="root1" password="123456" authorities="ROLE_USER, ROLE_ADMIN"/>
<security:user name="root2" password="123456" authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
使用root1登录,跳转到index.jsp
使用root2登录,跳转到noauthority.jsp
例子2,从数据库查询用户(非自定义数据库)
pom.xml增加数据库依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
spring-security.xml认证配置修改
<security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource"/>
</security:authentication-provider>
</security:authentication-manager>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
创建表
#用户表
create table users(
username VARCHAR(50) not null primary key,
password VARCHAR(50) not null,
enabled boolean not null
);
#权限表
create table authorities (
username VARCHAR(50) not null,
authority VARCHAR(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
#权限表username,authority两个字段创建唯一索引(可以不用)
create unique index ix_auth_username on authorities (username,authority);
insert into users(username,password,enabled) values('admin','admin',true);
insert into users(username,password,enabled) values('user','user',true);
insert into authorities(username,authority) values('admin','ROLE_ADMIN');
insert into authorities(username,authority) values('admin','ROLE_USER');
insert into authorities(username,authority) values('user','ROLE_USER');
security.xml 配置属性部分介绍
Spring EL表达式
表达式 | 描述 |
---|---|
hasRole([role]) | 当前用户是否拥有指定角色。 |
hasAnyRole([role1,role2]) | 多个角色是一个以逗号进行分隔的字符串。如果当前用户拥有指定角色中的任意一个则返回true。 |
hasAuthority([auth]) | 等同于hasRole |
hasAnyAuthority([auth1,auth2]) | 等同于hasAnyRole |
Principle | 代表当前用户的principle对象 |
authentication | 直接从SecurityContext获取的当前Authentication对象 |
permitAll | 总是返回true,表示允许所有的 |
denyAll | 总是返回false,表示拒绝所有的 |
isAnonymous() | 当前用户是否是一个匿名用户 |
isRememberMe() | 表示当前用户是否是通过Remember-Me自动登录的 |
isAuthenticated() | 表示当前用户是否已经登录认证成功了。 |
isFullyAuthenticated() | 如果当前用户既不是一个匿名用户,同时又不是通过Remember-Me自动登录的,则返回true。 |
<!-- 指定login.jsp安全性为none-->
<security:http security="none" pattern="/login.jsp" />
<security:http auto-config="true">
<security:form-login
<!-- 指定自定义登录页 -->
login-page="/login.jsp"
<!-- 指定提action,默认为login-->
login-processing-url="/login.do"
<!-- 指定登陆成功后跳转页 -->
default-target-url="/success.jsp"
<!--登录失败跳转页,默认会返回登录页,要配置成未登录也可以访问-->
authentication-failure-url="/login_failure.jsp"
<!-- 认证成功处理 -->
authentication-success-handler-ref ="authSuccess"
<!-- 认证失败处理,配置后authentication-failure-url属性将不再发生作用 -->
authentication-failure-handler-ref="authFailure"
<!-- 指定用户名参数,默认为username -->
username-parameter="username"
<!-- 指定密码,默认为password -->
password-parameter="password" />
<!-- 为所有请求配置权限 -->
<security:intercept-url pattern="/**" access="ROLE_USER" />
</security:http>
<!-- 认证成功后的处理类 -->
<bean id="authSuccess" class="com.xxx.AuthenticationSuccessHandlerImpl"/>
<!-- 认证成功后的处理类 -->
<bean id="authFailure" class="com.xxx.AuthenticationFailureHandlerImpl"/>
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
}
}
public class AuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
}
}