springboot+shiro实现自定义header登录认证

springboot+shiro实现自定义header登录认证

基本配置

config子包的ShiroConfig引入了Shiro并配置了shirFilter、realm和sessionManager;
shiroFilter配置只允许少量url可以匿名访问,其他url都需要登录才能访问;
realm设置的是shiro子包的AdminAuthorizingRealm类,该类作用是认证和授权的功能;
sessionManager设置的是shiro子包的AdminWebSessionManager类,该类作用是重置会话管理器。
默认会话管理器是基于cookie实现会话保持,而这里是基于自定义头部实现会话保持。
经过以上步骤,shiro就配置正常。
当管理员登录时,先认证;
认证成功,则授权,在后端内保存roles和permissions;同时,在响应头部写入自定义头部和sessionId;
认证失败,则抛出认证异常;
管理员再次访问页面时,shiro通过自定义头自动认证成功。

ShiroConfig 类代码如下

package com.cnlink.authdemo.common;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 *
 * @Description:TODO(这里用一句话描述这个类的作用)
 *
 * @author: xc.deng
 *
 * @date:2019年7月1日 上午10:32:51
 *
 */
@Configuration
public class ShiroConfig {
    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
     *
     * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
     * 3、部分过滤器可指定参数,如perms,roles
     *
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/admin/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/cpe/accountManage");
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        // 拦截器.
        Map filterChainDefinitionMap = new LinkedHashMap();
        // 配置不会被拦截的链接 顺序判断
//        filterChainDefinitionMap.put("/cas", "cas");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/admin/login", "anon");
        filterChainDefinitionMap.put("/register", "anon");
        filterChainDefinitionMap.put("/logout", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/captcha/kaptcha.jpg", "anon");

        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
//        filterChainDefinitionMap.put("/logout", "logout");
//        filterChainDefinitionMap.put("/add", "perms[权限添加]");
        // :这是一个坑呢,一不小心代码就不好使了;
        // 
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        System.out.println("Shiro拦截器工厂类注入成功");

        return shiroFilterFactoryBean;
    }


    /**
     * 身份认证realm; (这个需要自己写,账号密码校验;权限等)
     *
     * @return
     */
    @Bean
    public SecurityRealm myShiroRealm() {
        return new SecurityRealm();
    }


    @Bean
    public SessionManager sessionManager() {

        return new AdminWebSessionManager();
    }

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
//    @DependsOn("lifecycleBeanPostProcessor")
    public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

}

SecurityRealm 类代码如下

package com.cnlink.authdemo.common;
import com.cnlink.authdemo.model.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;

import javax.security.auth.login.AccountException;
import java.util.HashSet;
import java.util.Set;

/**
 * 用户身份验证,授权 Realm 组件
 * @Description:TODO(这里用一句话描述这个类的作用)
 *
 * @author: xc.deng
 *
 * @date:2019年7月1日 上午10:33:51
 *
 */
@Component(value = "securityRealm")
public class SecurityRealm extends AuthorizingRealm {

	/**
	 * 权限检查
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		System.out.println("权限认证方法:MyShiroRealm.doGetAuthenticationInfo()");
		System.out.println(principals.getPrimaryPrincipal());
		User token = (User)SecurityUtils.getSubject().getPrincipal();
	    SimpleAuthorizationInfo simpleAuthorizationInfo =  new SimpleAuthorizationInfo();
	    //实际开发,当前登录用户的角色和权限信息是从数据库来获取的,我这里写死是为了方便测试
	    Set roleSet = new HashSet();
	    roleSet.add("100002");
	    simpleAuthorizationInfo.setRoles(roleSet);
	    Set permissionSet = new HashSet();
	    permissionSet.add("admin:user:add");
	    simpleAuthorizationInfo.setStringPermissions(permissionSet);

	    return simpleAuthorizationInfo;
	}

	/**
	 * 登录验证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("身份认证方法:MyShiroRealm.doGetAuthenticationInfo()");

	    ShiroToken shiroToken = (ShiroToken) token;
	    User user = new User();
	    user.setUsername(shiroToken.getUserName());
	    user.setPassword(String.valueOf(shiroToken.getPassword()));
	    user.setStatus("1");
	    // 从数据库获取对应用户名密码的用户
//	    List userList = sysUserService.selectByMap(map);
//	    if(userList.size()!=0){
//	        user = userList.get(0);
//	    }
	    if (user==null) {
	        try {
				throw new AccountException("帐号或密码不正确!");

			} catch (AccountException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	    }else if("0".equals(user.getStatus())){
	        /**
	         * 如果用户的status为禁用。那么就抛出DisabledAccountException
	         */
	        throw new DisabledAccountException("帐号已经禁止登录!");
	    }else{
	        //更新登录时间 last login time
//	        user.setLastLoginTime(new Date());
//	        sysUserService.updateById(user);
	    }
	    return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
	}

}

ShiroToken 实体类代码如下

package com.cnlink.authdemo.common;

import org.apache.shiro.authc.UsernamePasswordToken;

public class ShiroToken extends UsernamePasswordToken{
	
	private String userName;

	private String captcha;



	public ShiroToken(String userName, String password) {
		super(userName,password);
		this.userName = userName;
	}

	public ShiroToken(String userName, String password,String captcha) {
		super(userName,password);
		this.userName = userName;
		this.captcha = captcha;
	}



	public String getUserName() {
		return userName;
	}



	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getCaptcha() {
		return captcha;
	}

	public void setCaptcha(String captcha) {
		this.captcha = captcha;
	}

	public Object getPrincipal() {
		return getUsername();
	}

	public Object getCredentials() {
		return getPassword();
	}

	
}

AdminWebSessionManager 类代码如下

package com.cnlink.authdemo.common;

import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

public class AdminWebSessionManager extends DefaultWebSessionManager {

    public static final String LOGIN_TOKEN_KEY = "X-Admin-Token";
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY);
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            return super.getSessionId(request, response);
        }
    }
}

你可能感兴趣的:(服务器后端)