spring-security4.2实现登录退出以及权限配置

最近用到了spring-security框架来实现登录验证。
以前做登录的步骤是:
1、用户输入用户名、密码登录
2、连接数据库对用户名、密码进行验证
3、获取用户信息(角色列表等等)
4、获取相关操作权限
security安全框架有点不同:
1、用户名、密码组合生成一个AuthenticationToken对象。
2、生成的这个token对象会传递给一个AuthenticationManager对象用于验证。
3、当成功认证后,AuthenticationManager返回一个Authentication对象。
4、接下来,就可以调用AuthenticationSuccessHandler成功处理器跳转首页或者登录之前访问的url。


先上spring-security.xml的配置


<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    <http pattern="/skin/**" security="none" />
    <http pattern="/login.action" security="none" />

    <http authentication-manager-ref="authenticationManager" entry-point-ref="customAuthenticationEntryPoint">
        
        <intercept-url pattern="/init/**" access="hasRole('ROLE_ADMIN')" />
        
        <intercept-url pattern="/login.action*" access="permitAll" />
        
        <intercept-url pattern="/user/*.action" access="hasRole('ROLE_ADMIN')" />
        
        <intercept-url pattern="/**" access="authenticated" />
        
        <custom-filter ref="customAuthenticationFilter" position="FORM_LOGIN_FILTER" />
        
        <logout logout-url="/logout.action" success-handler-ref="customLogoutSuccessHandler" />
        <headers>
            
            <frame-options disabled="true" />
        headers>
        <csrf disabled="true" />
    http>

    
    <authentication-manager alias="authenticationManager">
        <authentication-provider ref="customAuthenticationProvider" />
    authentication-manager>

    
    <beans:bean id="customAuthenticationProvider" class="com.identity.security.CustomAuthenticationProvider" />

    
    <beans:bean id="customAuthenticationEntryPoint" class="com.identity.security.CustomAuthenticationEntryPoint">
        <beans:constructor-arg name="pcLoginUrl" value="/login.action" />
    beans:bean>

    
    <beans:bean id="customAuthenticationFilter" class="com.identity.security.CustomAuthenticationFilter">
        <beans:constructor-arg name="filterProcessesUrl" value="/doLogin.action" />
        <beans:property name="authenticationSuccessHandler" ref="customAuthenticationSuccessHandler" />
        <beans:property name="authenticationFailureHandler" ref="customAuthenticationFailureHandler" />
        <beans:property name="authenticationManager" ref="authenticationManager" />
    beans:bean>

    
    <beans:bean id="customAuthenticationSuccessHandler" class="com.identity.security.CustomAuthenticationSuccessHandler" />

    
    <beans:bean id="customAuthenticationFailureHandler" class="com.identity.security.CustomAuthenticationFailureHandler" />

    
    <beans:bean id="customLogoutSuccessHandler" class="com.identity.security.CustomLogoutSuccessHandler" />

beans:beans>

先配置一个自定义登录页面

package com.identity.security;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

/**
 * 自定义认证入口点
 * 
 */
public class CustomAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {

    /**
     * 构造方法
     * 
     * @param pcLoginUrl 登录页
     */
    public CustomAuthenticationEntryPoint(String pcLoginUrl) {
        super(pcLoginUrl);
    }

    @Override
    protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
        return super.determineUrlToUseForThisRequest(request, response, exception);
    }
}

创建一个自定义的token对象类

package com.identity.security;

import org.springframework.security.authentication.AbstractAuthenticationToken;

import com.identity.entitys.UserInfo;

/**
 * 自定义认证token
 * 
 */
public class CustomAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = 1L;

    /** Principal */
    private Object principal;

    /** 帐号 */
    private String username;

    /** 密码 */
    private String password;

    /** 登录IP */
    private String loginIp;

    /** 构造方法,未通过登录认证 */
    public CustomAuthenticationToken() {
        super(null);
        this.principal = null;
        super.setAuthenticated(false);
    }

    /** 构造方法,已经通过登录认证 */
    public CustomAuthenticationToken(UserInfo user) {
        super(user.getAuthoritys());
        this.principal = user;
        super.setAuthenticated(true);
    }

    /**
     * 获取帐号
     * 
     * @return username 帐号
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置帐号
     * 
     * @param username 帐号
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取密码
     * 
     * @return password 密码
     */
    public String getPassword() {
        return password;
    }

    /**
     * 设置密码
     * 
     * @param password 密码
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 获取登录IP
     * 
     * @return loginIp 登录IP
     */
    public String getLoginIp() {
        return loginIp;
    }

    /**
     * 设置登录IP
     * 
     * @param loginIp 登录IP
     */
    public void setLoginIp(String loginIp) {
        this.loginIp = loginIp;
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return this.principal;
    }
}

用户输入完账号密码后会先进这个认证滤器生成我们定义好的token对象

package com.identity.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

/**
 * 自定义认证过滤器
 */
public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationFilter.class);

    /** 构造方法,设置登录URL */
    public CustomAuthenticationFilter(String filterProcessesUrl) {
        super(filterProcessesUrl);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        try {
            CustomAuthenticationToken token = new CustomAuthenticationToken();
            token.setUsername(request.getParameter("username"));
            token.setPassword(request.getParameter("password"));
            token.setLoginIp(getRequestIp(request));
            token.setDetails(authenticationDetailsSource.buildDetails(request));
            return this.getAuthenticationManager().authenticate(token);
        } catch (CustomAuthenticationException e) {
            throw e;
        } catch (Exception e) {
            LOGGER.error("登录过程异常,请求参数为[" + request + "]", e);
            throw new CustomAuthenticationException("登录失败,服务器内部错误,请稍后再试...");
        }
    }

    /** 获取请求客户端真实IP */
    public String getRequestIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

}

然后过滤器把这个Authentication对象传递给AuthenticationProvider去处理

package com.identity.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.transaction.annotation.Transactional;

import com.identity.entitys.LoginLog;
import com.identity.entitys.UserInfo;
import com.identity.querys.impl.UserInfoQueryImpl;

/**
 * 自定义认证服务提供者
 * 
 */
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationProvider.class);

    @Override
    @Transactional()
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        try {
            CustomAuthenticationToken token = (CustomAuthenticationToken) authentication;
            UserInfo user = retrieveUser(token);
            preAuthenticationCheck(token, user);
            additionalAuthenticationCheck(token, user);
            postAuthenticationCheck(token, user);
            saveLoginLog(token, user);
            CustomAuthenticationToken result = new CustomAuthenticationToken(user);
            result.setDetails(authentication.getDetails());
            return result;
        } catch (CustomAuthenticationException e) {
            throw e;
        } catch (Exception e) {
            LOGGER.error("登录认证异常,Token为[" + authentication + "]", e);
            throw new CustomAuthenticationException("登录失败,服务器内部错误,请稍后再试...", e);
        }
    }

    @Override
    public boolean supports(Class authentication) {
        return CustomAuthenticationToken.class.isAssignableFrom(authentication);
    }

    /** 检索用户 */
    private UserInfo retrieveUser(CustomAuthenticationToken token) {
    //这里是进数据库根据账号查用户
        UserInfo user = null;
        user = new UserInfoQueryImpl().username(token.getUsername(),false).uniqueResult();
        return user;
    }

    /** 前置的身份认证检查 */
    private void preAuthenticationCheck(CustomAuthenticationToken token, UserInfo user) {
        if (user == null) {
            throw new CustomAuthenticationException("登录失败,帐号不存在");
        }
        if (!user.isEnabled()) {
            throw new CustomAuthenticationException("登录失败,您的帐号已被禁用");
        }
        if (!user.isAccountNonExpired()) {
            throw new CustomAuthenticationException("登录失败,您的帐号已过期");
        }
    }

    /** 后置的身份认证检查 */
    private void postAuthenticationCheck(CustomAuthenticationToken token, UserInfo user) {
        if (!user.isCredentialsNonExpired()) {
            throw new CustomAuthenticationException("登录失败,您的密码已过期");
        }
    }

    /** 额外的身份认证检查 */
    public void additionalAuthenticationCheck(CustomAuthenticationToken token, UserInfo user) {
        if (!user.isRealPassword(token.getPassword())) {
            throw new CustomAuthenticationException("帐号或密码错误");
        }
    }

    /** 保存登录日志 */
    public void saveLoginLog(CustomAuthenticationToken token, UserInfo user) {
        LoginLog loginLog = new LoginLog();
        loginLog.setIp(token.getLoginIp());
        loginLog.setUser(user);
        loginLog.saveOrUpdateIt();
    }
}

在AuthenticationProvider里面验证后会进入登录成功或者失败处理器

package com.identity.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;

/**
 * 自定义登录认证成功处理器
 * 
 */
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        response.setContentType("text/html;charset=UTF-8");
        JSONObject jsonObject = new JSONObject();
        String targetUrl = request.getParameter("to");
        if (StringUtils.isEmpty(targetUrl)) {
            DefaultSavedRequest savedRequest = (DefaultSavedRequest) this.requestCache.getRequest(request, response);
            if (savedRequest != null) {
                targetUrl = savedRequest.getRequestURI() + "?" + savedRequest.getQueryString();
            } else {
                targetUrl = request.getContextPath() + "/index.action";
            }
        } else {
            this.requestCache.removeRequest(request, response);
        }
        clearAuthenticationAttributes(request);
        jsonObject.put("staut", true);
        jsonObject.put("targetUrl", targetUrl);
        response.getWriter().write(jsonObject.toString());
    }

    /** 删除身份认证临时数据 */
    protected final void clearAuthenticationAttributes(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return;
        }
        session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
    }
}
package com.identity.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

/**
 * 自定义登录认证失败处理器
 * 
 */
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("text/html;charset=UTF-8");
        String errorMsg = null;
        JSONObject jsonObject = new JSONObject();
        if (exception instanceof CustomAuthenticationException) {
            errorMsg = exception.getMessage();
        } else {
            LOGGER.error("登录异常,请求参数为[" + request + "]", exception);
            errorMsg = "登录失败,服务器内部错误,请稍后再试...";
        }
        jsonObject.put("staut", false);
        jsonObject.put("errorMsg", errorMsg);
        response.getWriter().write(jsonObject.toString());
    }
}

自定义异常类

package com.identity.security;

import org.springframework.security.core.AuthenticationException;

/**
 * 自定义认证异常
 * 
 */
public class CustomAuthenticationException extends AuthenticationException {

    private static final long serialVersionUID = 1L;

    public CustomAuthenticationException(String msg) {
        super(msg);
    }

    public CustomAuthenticationException(String msg, Throwable t) {
        super(msg, t);
    }
}

然后是退出

package com.identity.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

/**
 * 自定义退出登录处理器
 * 
 */
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("text/html;charset=UTF-8");
        JSONObject jsonObject = new JSONObject();
        String url = request.getParameter("to");
        if (StringUtils.isEmpty(url)) {
            url = request.getContextPath() + "/login.action?logout=true";
        }
        jsonObject.put("staut", true);
        jsonObject.put("url", url);
        response.getWriter().write(jsonObject.toString());
    }
}

然后用户和权限实体多对多的关系就行了,权限表的权限名记得是ROLE_XX的格式。完成以上的配置基本上登录退出就完成了~~

你可能感兴趣的:(路)