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

来源:互联网 发布:淘宝开店要交押金吗? 编辑:程序博客网 时间:2024/06/03 15:08

最近用到了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的格式。完成以上的配置基本上登录退出就完成了~~

阅读全文
1 0