一、实现功能
二、数据表设计
为了测试方便,这里创建一个简单的数据表,只含有name和password两个字段。至于角色,权限等,这里都先不考虑。
插入一条数据,name为admin,password为e10adc3949ba59abbe56e057f20f883e(这是123456经md5加密后得到的值)。
三、配置文件
1 在pom.xml中添加三个相关的包
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
2 web.xml中添加过滤器
<!-- 添加Spring-Security过滤器 -->
<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>/service/*</url-pattern>
</filter-mapping>
3 src/main/resource/spring/applicationContext-security.xml的内容为
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- 需要登陆能够访问的路径 -->
<http access-denied-page="/service/login/unSecurity" entry-point-ref="authenticationProcessingFilterEntryPoint">
<!-- 首页 -->
<intercept-url pattern="/service/index/index" access="ROLE_AUTHORITY"/>
<!-- 自定义loginFilter -->
<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" />
<logout logout-url="/service/login/logout" logout-success-url="/" invalidate-session="true"
delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE"/>
<session-management invalid-session-url="/service/login/unSecurity" session-authentication-strategy-ref="sas"/>
</http>
<!-- 登录验证器 -->
<beans:bean id="loginFilter" class="com.zheng.shared.security.JadeUserPwdAuthFilter">
<!-- 处理登录的action -->
<beans:property name="filterProcessesUrl" value="/service/login/userLogin"/>
<!-- 认证管理 点击完登录后,最终实现校验的是AuthenticationProvider-->
<beans:property name="authenticationManager" ref="myAuthenticationManager"/>
<!-- 验证成功后的处理-->
<beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"/>
<!-- 验证失败后的处理-->
<beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"/>
<!-- 实现多个帐号登录时最后一次登录的有效,目前只请允许登录一个帐号 -->
<beans:property name="sessionAuthenticationStrategy" ref="sas"/>
</beans:bean>
<beans:bean id="loginLogAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="alwaysUseDefaultTargetUrl" value="true"/>
<beans:property name="defaultTargetUrl" value="/service/login/loginSucc"/>
</beans:bean>
<beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<!-- 可以配置相应的跳转方式。属性forwardToDestination为true采用forward false为sendRedirect -->
<beans:property name="defaultFailureUrl" value="/service/login/loginFail"/>
</beans:bean>
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:property name="maximumSessions" value="1"/>
<beans:property name="exceptionIfMaximumExceeded" value="false"/>
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry"/>
</beans:bean>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<authentication-manager alias="myAuthenticationManager">
<authentication-provider ref="authenticationProvider"/>
</authentication-manager>
<beans:bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<!-- 配置异常能被捕捉 -->
<beans:property name="hideUserNotFoundExceptions" value="false" />
<beans:property name="userDetailsService" ref="userDetailService" />
<!-- <beans:property name="messageSource" ref="messageSource" /> -->
<!-- <beans:property name="userCache" ref="userCache" />可使用缓存保存用户信息-->
<!-- 开发过程中可以先把这两行注释掉-->
<!-- <beans:property name="passwordEncoder" ref="passwordEncode"/>
<beans:property name="saltSource" ref="saltSource" /> -->
</beans:bean>
<!-- 密码加密 -->
<beans:bean id="passwordEncode" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
<beans:bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<beans:property name="userPropertyToUse" value="id"/>
</beans:bean>
<beans:bean id="userDetailService" class="com.zheng.service.impl.UserServiceImpl" />
<!-- 未登录的切入点-->
<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/service/login/unSecurity" />
</beans:bean>
</beans:beans>
四、相关代码
1src/main/java/com/zheng/shared/sercurity/JadeUserPwdAuthFilter.java中的代码为
package com.zheng.shared.security;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.zheng.bean.User;
import com.zheng.dao.UserMapper;
public class JadeUserPwdAuthFilter extends UsernamePasswordAuthenticationFilter {
public static final String USERNAME = "userName";
public static final String PASSWORD = "userPassword";
@Autowired
private UserMapper userDao;
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String userName = request.getParameter(USERNAME);
String password = request.getParameter(PASSWORD);
User user = userDao.findUserByUserName(userName);
System.out.println("username: " + user.getUsername());
System.out.println("password: " + user.getPassword());
// 验证用户是否被启用
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password);
// 允许子类设置详细属性
setDetails(request, authRequest);
// 运行UserDetailsService的loadUserByUsername 再次封装Authentication
return this.getAuthenticationManager().authenticate(authRequest);
}
}
2 src/main/java/com/zheng/service/UserService.java的内容为
package com.zheng.service;
import org.springframework.security.core.userdetails.UserDetailsService;
public interface UserService extends UserDetailsService{
}
3 src/main/java/com/zheng/service/impl/UserServiceImpl.java的内容为
package com.zheng.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.zheng.bean.User;
import com.zheng.dao.UserMapper;
import com.zheng.service.UserService;
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = null;
try {
user = userMapper.findUserByUserName(username);
} catch (Exception e) {
e.printStackTrace();
}
if (user == null) {
throw new UsernameNotFoundException("用户名或密码不正确!");
}
System.out.println("username: " + user.getUsername());
System.out.println("password: " + user.getPassword());
return user;
}
}
4 src/main/java/com/zheng/bean/User.java的内容为
package com.zheng.bean;
import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements UserDetails , Serializable {
private static final long serialVersionUID = 123L;
private String userName;
private String password;
private Collection<GrantedAuthority> authorities;// 用户证书是否有效
@Override
public String getUsername() {
return this.userName;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public void setAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
特别需要注意的是:用户只有在不过期、没被锁定、没被禁用的情况下才能登录成功,所以isEnabled()方法的返回值设为真,表示用户没有禁用。
5 src/main/java/com/zheng/dao/UserMapper.java的内容为
package com.zheng.dao;
import com.zheng.bean.User;
public interface UserMapper {
/**
* 根据用户名查找
* @param userName
* @return
*/
User findUserByUserName(String name);
}
6 src/main/resources/config/mybatis/mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zheng.dao.UserMapper" >
<resultMap id="BaseResultMap" type="com.zheng.bean.User" >
<result column="name" property="userName" jdbcType="VARCHAR" />
<result column="password" property="password" jdbcType="VARCHAR" />
</resultMap>
<select id="findUserByUserName" parameterType="string" resultMap="BaseResultMap" >
select * from user where name = #{userName}
</select>
</mapper>
7 LoginController.java中响应登录成功和失败的方法为
/**
* 登陆成功进行处理的方法
* @param request
* @return
*/
@RequestMapping("/loginSucc")
@ResponseBody
public Map<String,Object> loginSucc(HttpServletRequest request){
System.out.println("登录成功!");
Map<String,Object> result = new HashMap<String,Object>();
return result;
}
/**
* 登陆失败进行的操作
* @param request
* @return
*/
@RequestMapping("/loginFail")
@ResponseBody
public Map<String,Object> loginFail(HttpServletRequest request){
System.out.println("登录失败!");
Map<String,Object> result = new HashMap<String,Object>();
return result;
}
五、运行结果