SpringBoot整合Shiro流程

1.pom.xml导入shiro相关jar包

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.4.0</version>
</dependency>
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-ehcache</artifactId>
	<version>1.4.0</version>
</dependency>

2.封装Shiro配置类

在配置类中Realm,DefaultWebSecurityManager,ShiroFilterFactoryBean这三个Bean必须要配置的。

@Configuration
public class ShiroConfiguration {
	@Bean
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);

		shiroFilterFactoryBean.setLoginUrl("/login");

		Map<String, Filter> filters = new LinkedHashMap<>();
		filters.put("authc", new JwtFilter());

		shiroFilterFactoryBean.setFilters(filters);
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
		// 登录接口排除
		filterChainDefinitionMap.put("/login", "anon");
		// 退出登录接口排除
		filterChainDefinitionMap.put("/logout", "anon");
		// 图形验证码接口排除
		filterChainDefinitionMap.put("/captcha", "anon");

		anon(filterChainDefinitionMap);
		filterChainDefinitionMap.put("/**", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	private void anon(Map<String, String> filterChainDefinitionMap) {
		filterChainDefinitionMap.put("/", "anon");
		filterChainDefinitionMap.put("/*.html", "anon");
		filterChainDefinitionMap.put("/favicon.ico", "anon");
		filterChainDefinitionMap.put("/css/**", "anon");
		filterChainDefinitionMap.put("/js/**", "anon");
		filterChainDefinitionMap.put("/beta/**", "anon");
		filterChainDefinitionMap.put("/fonts/**", "anon");
		filterChainDefinitionMap.put("/layui/**", "anon");
		filterChainDefinitionMap.put("/img/**", "anon");
		filterChainDefinitionMap.put("/v2/api-docs/**", "anon");
		filterChainDefinitionMap.put("/swagger-resources/**", "anon");
		filterChainDefinitionMap.put("/webjars/**", "anon");
		filterChainDefinitionMap.put("/pages/**", "anon");
		filterChainDefinitionMap.put("/druid/**", "anon");
		filterChainDefinitionMap.put("/statics/**", "anon");
	}

	@Bean
	public DefaultWebSecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		// 多realm
		Set<Realm> realms = new HashSet<>(MediaConstants.DEFAULT_COLLECTION_SIZE);
		realms.add(adminRealm());
		securityManager.setRealms(realms);
		// 关闭session
		DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
		DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
		sessionStorageEvaluator.setSessionStorageEnabled(false);
		subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
		securityManager.setSubjectDAO(subjectDAO);
		securityManager.setAuthenticator(authenticator());
		return securityManager;
	}

	@Bean
	public Authenticator authenticator() {
		ModularRealm modularRealm = new ModularRealm();
		modularRealm.setRealms(Arrays.asList(adminRealm()));
		modularRealm.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
		return modularRealm;
	}

	@Bean
	public AdminShiroRealm adminRealm() {
		AdminShiroRealm mobileRealm = new AdminShiroRealm();
		mobileRealm.setName(LoginAdminTypeEnums.ADMIN_USER.value);
		return mobileRealm;
	}


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

	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

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

}

3.配置Realm

Realm中主要涉及用户身份验证和授权策略信息的设置

@Data
public class AdminShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserAuthService userAuthMgr;

    /**
     * 使用JWT替代原生Token
     *
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    private static final String ADMIN_USER_LOGIN_TYPE = LoginAdminTypeEnums.ADMIN_USER.value;

    {
        // 设置realm的名字,非常重要
        super.setName(ADMIN_USER_LOGIN_TYPE);
    }

    /**
     * 功能: 获取用户权限信息,包括角色以及权限。只有当触发检测用户权限时才会调用此方法,例如checkRole,checkPermission
     *
     * @return AuthorizationInfo 权限信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String sid = JwtUtil.getSid(principals.toString());
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 查询用户权限
        List<String> userAuthList = userAuthMgr.getAuthority(sid);
        if (!CollectionUtils.isEmpty(userAuthList)) {
            Set<String> userAuthorities = new HashSet<>(userAuthList);
            authorizationInfo.setStringPermissions(userAuthorities);
        }

        return authorizationInfo;
    }

    /**
     * 功能: 用来进行身份认证,也就是说验证用户输入的账号和密码是否正确,获取身份验证信息,错误抛出异常
     *
     * @return 返回封装了用户信息的 AuthenticationInfo 实例
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        if (StringUtils.isEmpty(token)) {
            throw new AuthenticationException("token非法无效!");
        }
        String loginAccount = JwtUtil.getLoginAccount(token);
        String loginType = JwtUtil.getLoginType(token);
        if (loginAccount == null || loginType == null) {
            throw new AuthenticationException("token非法无效!");
        }
        if (!JwtUtil.verify(token, loginAccount)) {
            throw new AuthenticationException("token失效,请重新登录!");
        }
        return new SimpleAuthenticationInfo(token, token, getName());
    }
}

4.涉及到的工具类

/**
 * 鉴权登录拦截器
 *
 *
 */
public class JwtFilter extends BasicHttpAuthenticationFilter {

	private static final Logger logger = LoggerFactory.getLogger(JwtFilter.class);
	private static String AUTHORIZATION = "Authorization";
	
	@Override
	protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		String token = httpServletRequest.getHeader(AUTHORIZATION);
		String loginType = JwtUtil.getLoginType(token);
		JwtToken jwtToken = new JwtToken(token,loginType);
		 // 提交给realm进行登入,如果错误他会抛出异常并被捕获
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
	}
	
	

	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
		if (isLoginRequest(request, response)) {
			try {
				return super.onAccessDenied(request, response);
			} catch (Exception e) {
				logger.error("onAccessDenied Exception", e);
				return false;
			}
		}
        try {
    		// 接口需要登录
			return executeLogin(request, response);
		} catch (Exception e) {
			return false;
		}
	}

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		//访问拒绝的逻辑
		if (isLoginRequest(request, response)) {
			return false;
		} 
		// 未登录
		Subject subject = getSubject(request, response);
		if (subject == null || subject.getPrincipal() == null) {
			WebUtils.responseJsonText(response, ApiResponse.of("401", "un login"));
			return false;
		}
		return super.onAccessDenied(request, response);
	}

	@Override
	protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
			ServletResponse response) {
		logger.warn("onLoginFailure AuthenticationToken={},AuthenticationException={}", token);
		try {
			WebUtils.responseJsonText(response, ApiResponse.of("500", e.getMessage()));
		} catch (IOException e1) {
			logger.error("response error", e);
			return false;
		}
		return false;
	}

	@Override
	protected String getHost(ServletRequest request) {
		return WebUtils.getHost(request);
	}

}
public class JwtUtil {

	public static final String SID = "sid"; 
	
	public static final String LOGIN_ACCOUNT = "loginAccount";
	/**
	 * 机构id
	 */
	public static final String ORGANIZATION_ID = "organizationId"; 
	/**
	 * 登录类型
	 */
	public static final String LOGIN_TYPE = "loginType";

	/**
	 * 登录昵称
	 */
	public static final String LOGIN_NICKNAME = "loginNickname";

	/**
	 * 校验token是否正确
	 * 
	 * @param token  密钥
	 * @param loginAccount 登录账号
	 * @return 是否正确
	 */
	public static boolean verify(String token, String loginAccount) {
		try {
			Long userId = getUserId(token);
			Algorithm algorithm = Algorithm.HMAC256(Md5Utils.md5Hex(userId.toString()));
			JWTVerifier verifier = JWT.require(algorithm).withClaim(LOGIN_ACCOUNT, loginAccount).build();
			verifier.verify(token);
			return true;
		} catch (Exception exception) {
			return false;
		}
	}

	/**
	 * 获得token中的信息无需secret解密也能获得
	 * 
	 * @return token中包含的用户名
	 */
	public static String getLoginAccount(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim(LOGIN_ACCOUNT).asString();
		} catch (JWTDecodeException e) {
			return null;
		}
	}

	/**
	 * 获得token中的信息无需secret解密也能获得
	 *
	 * @return token中包含的用户名
	 */
	public static String getLoginNickname(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim(LOGIN_NICKNAME).asString();
		} catch (JWTDecodeException e) {
			return null;
		}
	}

	public static Long getUserId(String token) {
		if(StringUtils.isEmpty(token)) {
			return null;
		}
		try {
			DecodedJWT jwt = JWT.decode(token);
			return Long.parseLong(jwt.getSubject());
		} catch (JWTDecodeException e) {
			return null;
		}
	}
	
	public static String getSid(String token) {
		if(StringUtils.isEmpty(token)) {
			return null;
		}
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim(SID).asString();
		} catch (JWTDecodeException e) {
			return null;
		}
	}

	/**
	 * 后台管理员生成签名
	 * @param loginAccount 用户名
	 * @return 加密的token
	 */
	public static String signAdmin(String loginAccount,Long userId,String sid,String loginType,String nickName) {
		try {
			loginAccount = StringUtils.lowerCase(loginAccount);
			Algorithm algorithm = Algorithm.HMAC256(Md5Utils.md5Hex(userId.toString()));
			return JWT.create()
					.withClaim(LOGIN_ACCOUNT, loginAccount)
					.withClaim(SID, sid)
					.withClaim(LOGIN_TYPE, loginType)
					.withClaim(LOGIN_NICKNAME,nickName)
					.withSubject(String.valueOf(userId))
					.withExpiresAt(new Date(System.currentTimeMillis() + CacheExpiresTime.ADMIN_EXPIRE_TIME_MILLISECOND))
					.sign(algorithm);
		} catch (Exception e) {
			return null;
		}
	}
	
	
	public static String getLoginType(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim(LOGIN_TYPE).asString();
		} catch (JWTDecodeException e) {
			return null;
		}
	}
	
	/**
	 * 
	 * title: 获取当前登录用户的机构id
	 * @param token
	 * @return
	 */
	public static Long getOrganizationId(String token) {
		if(StringUtils.isEmpty(token)) {
			return null;
		}
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim(ORGANIZATION_ID).asLong();
		} catch (JWTDecodeException e) {
			return null;
		}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值