1. 配置文件
WEB-INF/web.xml
<!-- SpringSecurity过滤器链 -->
<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 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
classpath:spring-security.xml
</param-value>
</context-param>
<!--启动SpringMVC-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 服务器启动加载Servlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
springmvc.xml
<!-- 扫描Controller类-->
<contenxt:component-scan base-package="cn.bug"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--注解方式处理器映射器和处理器适配器 -->
<mvc:annotation-driven/>
2. 使用HttpBasic方式进行登录(认证)
pattern: 需要拦截资源
access: 拦截方式
isFullyAuthenticated(): 该资源需要认证才可以访问
isAnonymous():只有匿名用户才可以访问(如果登录用户就无法访问)
permitAll():允许所有人(匿名和登录用户)方法
<security:http>
<security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<!--使用HttpBasic方式进行登录(认证)-->
<security:http-basic/>
</security:http>
<!--
security:authentication-manager: 认证管理器
1)认证信息提供方式(账户名,密码,当前用户权限)
-->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="root" password="123456" authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
在游览器弹出的输入框里面,输入正确的用户名密码即可登陆
3. form-login属性详解
form-login是spring security命名空间配置登录相关信息的标签,它包含如下属性:
- login-page 自定义登录页url,默认为/login
- login-processing-url 登录请求拦截的url,也就是form表单提交时指定的action
- default-target-url 默认登录成功后跳转的url
- always-use-default-target 是否总是使用默认的登录成功后跳转url
- authentication-failure-url 登录失败后跳转的url
- username-parameter 用户名的请求字段,默认为username
- password-parameter 密码的请求字段,默认为password
- authentication-success-handler-ref 指向一个AuthenticationSuccessHandler用于处理认证成功的请求,不能和default-target-url还有always-use-default-target同时使用
- authentication-failure-handler-ref 指向一个AuthenticationFailureHandler用于处理失败的认证请求
- authentication-success-forward-url 用于authentication-failure-handler-ref
- authentication-failure-forward-url 用于authentication-failure-handler-ref
- authentication-details-source-ref 指向一个AuthenticationDetailsSource,在认证过滤器中使用
处理拦截的几个Filter
UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication//打断点
String username = obtainUsername(request);
String password = obtainPassword(request);
ExceptionTranslationFilter
org.springframework.security.web.access.ExceptionTranslationFilter#doFilter//打断点
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
FilterSecurityInterceptor
org.springframework.security.web.access.intercept.FilterSecurityInterceptor#invoke//打断点
InterceptorStatusToken token = super.beforeInvocation(fi);
3. 为每个用户指定不同的权限
<security:http>
<security:intercept-url pattern="/product/index" access="permitAll()"/>
<security:intercept-url pattern="/userLogin" access="permitAll()"/>
<security:intercept-url pattern="/product/add" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/product/update" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/product/list" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/product/delete" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<security:form-login login-page="/userLogin" login-processing-url="/securityLogin"
default-target-url="/product/index"/>
<!-- 自定义权限不足处理 -->
<security:access-denied-handler error-page="/error"/>
<!-- 关闭Spring Security CSRF机制 -->
<security:csrf disabled="true"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="root" password="123456" authorities="ROLE_USER"/>
<security:user name="jack" password="123456" authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
4. 用户的用户名密码通过java类来实现
<security:http>
<security:intercept-url pattern="/product/index" access="permitAll()"/>
<security:intercept-url pattern="/userLogin" access="permitAll()"/>
<security:intercept-url pattern="/product/add" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/product/update" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/product/list" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/product/delete" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<security:form-login login-page="/userLogin" login-processing-url="/securityLogin"
default-target-url="/product/index"/>
<!-- 自定义权限不足处理 -->
<security:access-denied-handler error-page="/error"/>
<!-- 关闭Spring Security CSRF机制 -->
<security:csrf disabled="true"/>
</security:http>
<security:authentication-manager>
<!-- 自定义UserDetailService方式-->
<security:authentication-provider user-service-ref="myUserDetailService"/>
</security:authentication-manager>
<bean id="myUserDetailService" class="cn.bug.security.MyUserDetailService"/>
同步方式进行页面跳转(无法满足前后端分离开发)
public class MyUserDetailService implements UserDetailsService {
/**
* loadUserByUsername: 读取用户信息
* @return 实现UserDetails即可
*
* ROLE_USER,ROLE_ADMIN
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//UserDetails: 封装用户数据的接口
//操作数据库等等...
return new User( "bug", "123456", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
}
5. 返回自定义信息
<security:http>
<security:intercept-url pattern="/product/index" access="permitAll()"/>
<security:intercept-url pattern="/userLogin" access="permitAll()"/>
<security:intercept-url pattern="/product/add" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/product/update" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/product/list" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/product/delete" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<security:form-login login-page="/userLogin" login-processing-url="/securityLogin"
default-target-url="/product/index"
authentication-success-handler-ref="myAuthenticationSuccessHandler"
authentication-failure-handler-ref="myAuthenticationFailureHandler"/>
<!-- 自定义权限不足处理 -->
<security:access-denied-handler error-page="/error"/>
<!-- 关闭Spring Security CSRF机制 -->
<security:csrf disabled="true"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="myUserDetailService"/>
</security:authentication-manager>
<bean id="myUserDetailService" class="cn.bug.security.MyUserDetailService"/>
<bean id="myAuthenticationSuccessHandler" class="cn.bug.security.MyAuthenticationSuccessHandler"/>
<bean id="myAuthenticationFailureHandler" class="cn.bug.security.MyAuthenticationFailureHandler"/>
/**
* 验证失败后返回的自定义数据类型
*/
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
/**
* ObjectMapper: jackson框架的工具类,用于转换对象为json字符串
* <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
* <dependency>
* <groupId>com.fasterxml.jackson.core</groupId>
* <artifactId>jackson-databind</artifactId>
* <version>2.9.5</version>
* </dependency>
*/
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//返回json字符串给前端
Map<String, Object> result = new HashMap<>();
result.put("succcess", false);
String json = objectMapper.writeValueAsString(result);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(json);
}
}
/**
* 登陆成功
*/
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private ObjectMapper objectMapper = new ObjectMapper();
/**
* @param authentication :代表认证成功后的信息
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//返回json字符串给前端
Map<String,Object> result = new HashMap<>();
result.put("succcess",true);
String json = objectMapper.writeValueAsString(result);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(json);
}
}
public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User( "eric","123456",AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
}
6. 原理分析
//1.整体流程
用户
||
\/
UsernamePasswordAuthenticationFilter
||
\/
AuthenticationManager
||
\/
AuthenticationProvider ---认证失败---> AuthenticationFailureHandler
||
认证成功
\/
AuthenticationSuccessHandler//2.源码分析
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication
return this.getAuthenticationManager().authenticate(authRequest);
||
\/
org.springframework.security.authentication.ProviderManager#authenticate
result = provider.authenticate(authentication);
||
\/
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate
org.springframework.security.authentication.AccountStatusUserDetailsChecker#check
7. UserDetails
/**
* 所有方法都需要返回true才能通过
*/
public class User implements UserDetails {
/**
* 返回当前用户所拥有的所有权限
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return null;
}
/**
* 改账号是否过期
* @return true不过期 false过期
*/
@Override
public boolean isAccountNonExpired() {
return false;
}
/**
* 账户是否锁定
*/
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
/**
* 账号是否可用
*/
@Override
public boolean isEnabled() {
return false;
}
}
return new User( "eric","123456", true,true,false,true,AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
<properties>
<jdk.version>1.8</jdk.version>
<spring.version>4.3.10.RELEASE</spring.version>
<spring.security.version>4.2.3.RELEASE</spring.security.version>
<jstl.version>1.2</jstl.version>
<servlet.version>2.5</servlet.version>
</properties>
<dependencies>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<!-- jstl for jsp page -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
</dependencies>