欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-19 14:06:22
...

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


先上spring-security.xml的配置

<?xml version="1.0" encoding="UTF-8"?>
<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" />
        <!-- 用户管理(如果多个权限可以访问就用hasAnyRole('xx','cc')) -->
        <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>
            <!-- Iframe页面允许被其它页面嵌入 -->
            <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的格式。完成以上的配置基本上登录退出就完成了~~

相关标签: security 框架