Spring Security實現後臺管理員登入(一)
阿新 • • 發佈:2019-01-03
一、實現功能
二、資料表設計
為了測試方便,這裡建立一個簡單的資料表,只含有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;
}
五、執行結果